From d612aea416fc0e113d3ba23c10c6682bc04b5f57 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 3 Nov 2024 21:09:05 -0600 Subject: [PATCH 01/16] Addition of room owners - buggy --- client/multiplayer/room.jsx | 35 +++++++++++++++++---- client/multiplayer/room.min.js | 4 +-- client/scripts/upsertPlayerItem.js | 20 +++++++++--- quizbowl/Player.js | 5 ++- server/multiplayer/ServerTossupRoom.js | 9 ++++-- server/multiplayer/handle-wss-connection.js | 8 ++--- 6 files changed, 62 insertions(+), 19 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index f4abb288b..b0224b53a 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -11,7 +11,7 @@ import upsertPlayerItem from '../scripts/upsertPlayerItem.js'; const categoryManager = new CategoryManager(); let oldCategories = JSON.stringify(categoryManager.export()); let startingDifficulties = []; - +let ownerId = "" let maxPacketNumber = 24; /** @@ -66,6 +66,7 @@ socket.onmessage = function (event) { case 'lost-buzzer-race': return lostBuzzerRace(data); case 'next': return next(data); case 'no-questions-found': return noQuestionsFound(data); + case 'owner-check': return ownerCheck(data); case 'pause': return pause(data); case 'reveal-answer': return revealAnswer(data); case 'set-categories': return setCategories(data); @@ -164,11 +165,8 @@ function connectionAcknowledged ({ document.getElementById('private-chat-warning').innerHTML = 'This is a permanent room. Some settings have been restricted.'; } - Object.keys(messagePlayers).forEach(userId => { - messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; - players[userId] = messagePlayers[userId]; - upsertPlayerItem(players[userId], USER_ID); - }); + processPlayers(messagePlayers); + sortPlayerListGroup(); switch (questionProgress) { @@ -220,6 +218,17 @@ function connectionAcknowledged ({ USER_ID = userId; window.localStorage.setItem('USER_ID', USER_ID); } +async function processPlayers(messagePlayers) { + const owner_id = await get_owner_id(); + + await Promise.all(Object.keys(messagePlayers).map(async (userId) => { + messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; + players[userId] = messagePlayers[userId]; + let is_owner = userId === owner_id; + upsertPlayerItem(players[userId], USER_ID, is_owner); + })); +} + async function connectionAcknowledgedQuery ({ difficulties = [], @@ -500,6 +509,20 @@ function noQuestionsFound () { window.alert('No questions found'); } +function ownerCheck({ id }) { + ownerId = id; +} + +let resolveOwnerId; +function get_owner_id() { + return new Promise((resolve) => { + resolveOwnerId = resolve; + socket.send(JSON.stringify({ type: 'owner-id' })); + }).then(() => { + return ownerId; + }); +} + function pause ({ paused, username }) { logEvent(username, `${paused ? '' : 'un'}paused the game`); } diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 178883461..5a24a9dbc 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,5 @@ -import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],maxPacketNumber=24;/** +import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24;/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b)}};function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),Object.keys(d).forEach(a=>{d[a].celerity=d[a].celerity.correct.average,players[a]=d[a],upsertPlayerItem(players[a],USER_ID)}),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b)}};function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await get_owner_id();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c];upsertPlayerItem(players[c],USER_ID,c===b)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}).then(()=>ownerId)}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,01>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index f9579406a..9b38aafcc 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -4,11 +4,16 @@ import { escapeHTML } from './utilities/strings.js'; * Upserts a player item to the DOM element with the id `player-list-group`. * @param {Player} player * @param {string} USER_ID - The item is highlighted blue if `USER_ID === player.userId`. + * @param {boolean} isOwner - This is who the owner of the room is */ -export default function upsertPlayerItem (player, USER_ID) { +export default function upsertPlayerItem (player, USER_ID, isOwner) { const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; - + //change later? + let res = "No"; + if (isOwner) { + res = "Yes" + } if (document.getElementById('list-group-' + userId)) { document.getElementById('list-group-' + userId).remove(); } @@ -16,13 +21,15 @@ export default function upsertPlayerItem (player, USER_ID) { const playerItem = document.createElement('a'); playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; playerItem.id = `list-group-${userId}`; + + let ifOwner = isOwner ? 'data-owner="true" style="color: red;"' : ""; playerItem.innerHTML = `
- ${escapeHTML(username)} + ${escapeHTML(username)} ${points}
`; - + playerItem.setAttribute('data-bs-container', 'body'); playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); playerItem.setAttribute('data-bs-html', 'true'); @@ -54,6 +61,11 @@ export default function upsertPlayerItem (player, USER_ID) { Celerity ${celerity.toFixed(3)} +
  • + Is Owner? + ${res} +
  • + `); diff --git a/quizbowl/Player.js b/quizbowl/Player.js index f4647ddf7..2b3c26dc0 100644 --- a/quizbowl/Player.js +++ b/quizbowl/Player.js @@ -4,7 +4,7 @@ class Player { this.MAX_USERNAME_LENGTH = MAX_USERNAME_LENGTH; this.username = ''; - + this.owner = false; this.powers = 0; this.tens = 0; this.zeroes = 0; @@ -22,6 +22,9 @@ class Player { } }; } + assignOwner() { + this.owner = true + } clearStats () { this.powers = 0; diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 1f7b769ce..14a98c163 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -13,8 +13,9 @@ import getNumPackets from '../../database/qbreader/get-num-packets.js'; import checkAnswer from 'qb-answer-checker'; export default class ServerTossupRoom extends TossupRoom { - constructor (name, isPermanent = false, categories = [], subcategories = [], alternateSubcategories = []) { + constructor (name, ownerId, isPermanent = false, categories = [], subcategories = [], alternateSubcategories = []) { super(name, categories, subcategories, alternateSubcategories); + this.ownerId = ownerId this.isPermanent = isPermanent; this.checkAnswer = checkAnswer; this.getNumPackets = getNumPackets; @@ -42,12 +43,13 @@ export default class ServerTossupRoom extends TossupRoom { case 'toggle-lock': return this.toggleLock(userId, message); case 'toggle-login-required': return this.toggleLoginRequired(userId, message); case 'toggle-public': return this.togglePublic(userId, message); + case 'owner-id': return this.owner_id(this.ownerId) default: super.message(userId, message); } } connection (socket, userId, username) { - console.log(`Connection in room ${HEADER}${this.name}${ENDC} - userId: ${OKBLUE}${userId}${ENDC}, username: ${OKBLUE}${username}${ENDC} - with settings ${OKGREEN}${Object.keys(this.settings).map(key => [key, this.settings[key]].join(': ')).join('; ')};${ENDC}`); + console.log(`Connection in room ${HEADER}${this.name}${ENDC} - ID of owner: ${OKBLUE}${this.ownerId}${ENDC} - userId: ${OKBLUE}${userId}${ENDC}, username: ${OKBLUE}${username}${ENDC} - with settings ${OKGREEN}${Object.keys(this.settings).map(key => [key, this.settings[key]].join(': ')).join('; ')};${ENDC}`); const isNew = !(userId in this.players); if (isNew) { this.players[userId] = new ServerPlayer(userId); } @@ -108,6 +110,9 @@ export default class ServerTossupRoom extends TossupRoom { this.emitMessage({ type: 'join', isNew, userId, username, user: this.players[userId] }); } + owner_id (id) { + this.emitMessage({ type: 'owner-check', id }); + } chat (userId, { message }) { // prevent chat messages if room is public, since they can still be sent with API if (this.settings.public || typeof message !== 'string') { return false; } diff --git a/server/multiplayer/handle-wss-connection.js b/server/multiplayer/handle-wss-connection.js index 304a59a17..4bca13a7c 100644 --- a/server/multiplayer/handle-wss-connection.js +++ b/server/multiplayer/handle-wss-connection.js @@ -16,7 +16,7 @@ const DOMPurify = createDOMPurify(window); const tossupRooms = {}; for (const room of PERMANENT_ROOMS) { const { name, categories, subcategories } = room; - tossupRooms[name] = new ServerTossupRoom(name, true, categories, subcategories); + tossupRooms[name] = new ServerTossupRoom(name, 0, true, categories, subcategories); } /** @@ -25,12 +25,12 @@ for (const room of PERMANENT_ROOMS) { * @param {String} roomName * @returns {TossupRoom} */ -function createAndReturnRoom (roomName, isPrivate = false) { +function createAndReturnRoom (roomName, userId, isPrivate = false) { roomName = DOMPurify.sanitize(roomName); roomName = roomName?.substring(0, ROOM_NAME_MAX_LENGTH) ?? ''; if (!Object.prototype.hasOwnProperty.call(tossupRooms, roomName)) { - const newRoom = new ServerTossupRoom(roomName, false); + const newRoom = new ServerTossupRoom(roomName, userId, false); newRoom.settings.public = !isPrivate; tossupRooms[roomName] = newRoom; } @@ -68,7 +68,7 @@ export default function handleWssConnection (ws, req) { return false; } - const room = createAndReturnRoom(roomName, isPrivate); + const room = createAndReturnRoom(roomName, userId, isPrivate); if (room.settings.lock === true) { ws.send(JSON.stringify({ type: 'error', From 7d00a87f6afaa2fd50d4f898840e8ae788e83565 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 4 Nov 2024 09:15:14 -0600 Subject: [PATCH 02/16] Addition of room owners with prior bug fix -- still buggy --- client/multiplayer/room.jsx | 14 +++++++++++++- client/multiplayer/room.min.js | 2 +- client/scripts/upsertPlayerItem.js | 2 ++ server/multiplayer/ServerTossupRoom.js | 2 ++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index b0224b53a..2b907abac 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -220,6 +220,7 @@ function connectionAcknowledged ({ } async function processPlayers(messagePlayers) { const owner_id = await get_owner_id(); + console.log("Await done"); await Promise.all(Object.keys(messagePlayers).map(async (userId) => { messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; @@ -509,16 +510,27 @@ function noQuestionsFound () { window.alert('No questions found'); } +let resolveOwnerId; + function ownerCheck({ id }) { + console.log("Owner check received at base at set ownerId"); ownerId = id; + console.log("Owner ID found to be " + ownerId); + + // Resolve the Promise to indicate that ownerId is set + if (resolveOwnerId) { + resolveOwnerId(); + resolveOwnerId = null; // Clear resolveOwnerId to prevent unintended future calls + } } -let resolveOwnerId; function get_owner_id() { return new Promise((resolve) => { resolveOwnerId = resolve; socket.send(JSON.stringify({ type: 'owner-id' })); + console.log("Prepping send"); }).then(() => { + console.log("All good! then received, returning owner ID at " + ownerId); return ownerId; }); } diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 5a24a9dbc..c6ac2ceba 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,5 @@ import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24;/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b)}};function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await get_owner_id();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c];upsertPlayerItem(players[c],USER_ID,c===b)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}).then(()=>ownerId)}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b)}};function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await get_owner_id();console.log("Await done"),await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c];upsertPlayerItem(players[c],USER_ID,c===b)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"})),console.log("Prepping send")}).then(()=>(console.log("All good! then received, returning owner ID at "+ownerId),ownerId))}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,01>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index 9b38aafcc..fb840521f 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -7,9 +7,11 @@ import { escapeHTML } from './utilities/strings.js'; * @param {boolean} isOwner - This is who the owner of the room is */ export default function upsertPlayerItem (player, USER_ID, isOwner) { + console.log("Called"); const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; //change later? + console.log("isOwner : " + isOwner); let res = "No"; if (isOwner) { res = "Yes" diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 14a98c163..8ddb7d132 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -111,7 +111,9 @@ export default class ServerTossupRoom extends TossupRoom { } owner_id (id) { + console.log("Recieved a owner-id request"); this.emitMessage({ type: 'owner-check', id }); + console.log("Owner check sent"); } chat (userId, { message }) { // prevent chat messages if room is public, since they can still be sent with API From e67a1ca8983dd0a12a670c7aed4fb2b834e50fcc Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 4 Nov 2024 17:46:30 -0600 Subject: [PATCH 03/16] Full implementation of the ban feature with memory and some bug fixing. Could still be issues. --- client/multiplayer/room.jsx | 34 ++++++- client/multiplayer/room.min.js | 4 +- client/scripts/upsertPlayerItem.js | 133 +++++++++++++------------ server/multiplayer/ServerTossupRoom.js | 18 ++++ 4 files changed, 119 insertions(+), 70 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index 2b907abac..ae47e693c 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -49,6 +49,7 @@ socket.onclose = function (event) { socket.onmessage = function (event) { const data = JSON.parse(event.data); switch (data.type) { + case 'enforcing-ban': return ackBannedFromRoom(); case 'buzz': return buzz(data); case 'force-username': return forceUsername(data); case 'chat': return chat(data, false); @@ -90,8 +91,28 @@ socket.onmessage = function (event) { case 'toggle-standard-only': return toggleStandardOnly(data); case 'toggle-timer': return toggleTimer(data); case 'update-question': return updateQuestion(data); + case 'verified-ban': return recvBan(data); } }; +function ackBannedFromRoom() { + window.alert("You were banned from this room by the room owner, and cannot rejoin it."); + setTimeout(() => { + window.location.replace("../"); + }, 100); + +} +function recvBan({target, targetUsername}) { + if (target === USER_ID) { + window.alert("You were banned from this room by the room owner."); + setTimeout(() => { + window.location.replace("../"); + }, 100); + } else { + logEvent(targetUsername + " has been banned from this room."); + } + +} + function buzz ({ userId, username }) { logEvent(username, 'buzzed'); @@ -142,7 +163,7 @@ function clearStats ({ userId }) { for (const field of ['celerity', 'negs', 'points', 'powers', 'tens', 'tuh', 'zeroes']) { players[userId][field] = 0; } - upsertPlayerItem(players[userId], USER_ID); + upsertPlayerItem(players[userId], USER_ID, ownerId, socket); sortPlayerListGroup(); } @@ -220,13 +241,14 @@ function connectionAcknowledged ({ } async function processPlayers(messagePlayers) { const owner_id = await get_owner_id(); + console.log("Await done"); await Promise.all(Object.keys(messagePlayers).map(async (userId) => { messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; players[userId] = messagePlayers[userId]; - let is_owner = userId === owner_id; - upsertPlayerItem(players[userId], USER_ID, is_owner); + + upsertPlayerItem(players[userId], USER_ID, owner_id, socket); })); } @@ -337,7 +359,7 @@ async function giveAnswer ({ celerity, directive, directedPrompt, givenAnswer, p players[userId].tuh++; players[userId].celerity = celerity; - upsertPlayerItem(players[userId], USER_ID); + upsertPlayerItem(players[userId], USER_ID, ownerId, socket); sortPlayerListGroup(); } @@ -368,7 +390,7 @@ function join ({ isNew, user, userId, username }) { if (isNew) { user.celerity = user.celerity.correct.average; - upsertPlayerItem(user, USER_ID); + upsertPlayerItem(user, USER_ID, ownerId, socket); sortPlayerListGroup(); players[userId] = user; } else { @@ -633,12 +655,14 @@ function setUsername ({ oldUsername, newUsername, userId }) { document.getElementById('username-' + userId).textContent = newUsername; players[userId].username = newUsername; sortPlayerListGroup(); + if (userId === USER_ID) { username = newUsername; window.localStorage.setItem('multiplayer-username', username); document.getElementById('username').value = username; } + upsertPlayerItem(players[userId], USER_ID, ownerId, socket); } function toggleLock ({ lock, username }) { diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index c6ac2ceba..6e2d1e022 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,5 @@ import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24;/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b)}};function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await get_owner_id();console.log("Await done"),await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c];upsertPlayerItem(players[c],USER_ID,c===b)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"})),console.log("Prepping send")}).then(()=>(console.log("All good! then received, returning owner ID at "+ownerId),ownerId))}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username -if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,01>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"enforcing-ban":return ackBannedFromRoom();case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await get_owner_id();console.log("Await done"),await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"})),console.log("Prepping send")}).then(()=>(console.log("All good! then received, returning owner ID at "+ownerId),ownerId))}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,01>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index fb840521f..13c91f4c9 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -4,75 +4,82 @@ import { escapeHTML } from './utilities/strings.js'; * Upserts a player item to the DOM element with the id `player-list-group`. * @param {Player} player * @param {string} USER_ID - The item is highlighted blue if `USER_ID === player.userId`. - * @param {boolean} isOwner - This is who the owner of the room is + * @param {string} ownerId - ID of the room owner */ -export default function upsertPlayerItem (player, USER_ID, isOwner) { - console.log("Called"); - const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; - const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; - //change later? - console.log("isOwner : " + isOwner); - let res = "No"; - if (isOwner) { - res = "Yes" - } - if (document.getElementById('list-group-' + userId)) { - document.getElementById('list-group-' + userId).remove(); - } +export default function upsertPlayerItem(player, USER_ID, ownerId, socket) { + if (!player || !player.userId) { + console.error("Player or player.userId is undefined", { player }); + return; + } - const playerItem = document.createElement('a'); - playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; - playerItem.id = `list-group-${userId}`; + const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; + const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; + + const playerIsOwner = ownerId === userId; + const iAmOwner = ownerId === USER_ID; + const res = playerIsOwner ? "Yes" : "No"; - let ifOwner = isOwner ? 'data-owner="true" style="color: red;"' : ""; - playerItem.innerHTML = ` + // Remove the existing player item if it exists + if (document.getElementById('list-group-' + userId)) { + document.getElementById('list-group-' + userId).remove(); + } + + + const playerItem = document.createElement('a'); + playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; + playerItem.id = `list-group-${userId}`; + + + const displayUsername = playerIsOwner ? `👑 ${escapeHTML(username)}` : escapeHTML(username); + + playerItem.innerHTML = `
    - ${escapeHTML(username)} + ${displayUsername} ${points}
    - `; - - playerItem.setAttribute('data-bs-container', 'body'); - playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); - playerItem.setAttribute('data-bs-html', 'true'); - playerItem.setAttribute('data-bs-placement', 'left'); - playerItem.setAttribute('data-bs-toggle', 'popover'); - playerItem.setAttribute('data-bs-trigger', 'focus'); - playerItem.setAttribute('tabindex', '0'); + `; + + // Set attributes for the popover + playerItem.setAttribute('data-bs-container', 'body'); + playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); + playerItem.setAttribute('data-bs-html', 'true'); + playerItem.setAttribute('data-bs-placement', 'left'); + playerItem.setAttribute('data-bs-toggle', 'popover'); + playerItem.setAttribute('data-bs-trigger', 'focus'); + playerItem.setAttribute('tabindex', '0'); + + // Popover content + playerItem.setAttribute('data-bs-title', username); + playerItem.setAttribute('data-bs-content', ` +
      +
    • Powers${powers}
    • +
    • Tens${tens}
    • +
    • Negs${negs}
    • +
    • TUH${tuh}
    • +
    • Celerity${celerity.toFixed(3)}
    • +
    • Is Owner?${res}
    • +
    + `); + + + document.getElementById('player-list-group').appendChild(playerItem); + + //ban button if the viewer is the owner and the player is not + if (iAmOwner && userId !== ownerId) { + const banButton = document.createElement('button'); + banButton.className = 'btn btn-danger btn-sm mt-2'; + banButton.innerText = 'Ban'; + + + playerItem.appendChild(banButton); - playerItem.setAttribute('data-bs-title', username); - playerItem.setAttribute('data-bs-content', ` -
      -
    • - Powers - ${powers} -
    • -
    • - Tens - ${tens} -
    • -
    • - Negs - ${negs} -
    • -
    • - TUH - ${tuh} -
    • -
    • - Celerity - ${celerity.toFixed(3)} -
    • -
    • - Is Owner? - ${res} -
    • - -
    - `); + + banButton.addEventListener('click', () => { + console.log(`Banning user ${username} (ID: ${userId})`); + socket.send(JSON.stringify({ type: 'ban', ownerId: ownerId, target_user: userId, targ_name: username })); + }); + } - document.getElementById('player-list-group').appendChild(playerItem); - // bootstrap requires "new" to be called on each popover - // eslint-disable-next-line no-new - new bootstrap.Popover(playerItem); + // popover + new bootstrap.Popover(playerItem); } diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 8ddb7d132..1d2d923ad 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -22,6 +22,7 @@ export default class ServerTossupRoom extends TossupRoom { this.getRandomTossups = getRandomTossups; this.getSet = getSet; this.getSetList = getSetList; + this.bannedUserList = []; this.rateLimiter = new RateLimit(50, 1000); this.rateLimitExceeded = new Set(); @@ -37,6 +38,7 @@ export default class ServerTossupRoom extends TossupRoom { async message (userId, message) { switch (message.type) { + case 'ban': return this.banUser(message.ownerId, message.target_user, message.targ_name); case 'chat': return this.chat(userId, message); case 'chat-live-update': return this.chatLiveUpdate(userId, message); case 'give-answer-live-update': return this.giveAnswerLiveUpdate(userId, message); @@ -49,6 +51,8 @@ export default class ServerTossupRoom extends TossupRoom { } connection (socket, userId, username) { + + console.log(`Connection in room ${HEADER}${this.name}${ENDC} - ID of owner: ${OKBLUE}${this.ownerId}${ENDC} - userId: ${OKBLUE}${userId}${ENDC}, username: ${OKBLUE}${username}${ENDC} - with settings ${OKGREEN}${Object.keys(this.settings).map(key => [key, this.settings[key]].join(': ')).join('; ')};${ENDC}`); const isNew = !(userId in this.players); @@ -56,6 +60,11 @@ export default class ServerTossupRoom extends TossupRoom { this.players[userId].online = true; this.sockets[userId] = socket; username = this.players[userId].safelySetUsername(username); + if (this.bannedUserList.includes(userId)) { + console.log("Banned user " + userId + " (" + username + ") tried to join a room"); + this.sendToSocket(userId, {type: 'enforcing-ban'}) + return; + } socket.on('message', message => { if (this.rateLimiter(socket) && !this.rateLimitExceeded.has(username)) { @@ -74,6 +83,7 @@ export default class ServerTossupRoom extends TossupRoom { }); socket.on('close', this.close.bind(this, userId)); + socket.send(JSON.stringify({ type: 'connection-acknowledged', @@ -109,6 +119,14 @@ export default class ServerTossupRoom extends TossupRoom { this.emitMessage({ type: 'join', isNew, userId, username, user: this.players[userId] }); } + banUser(ownerId, target_user, target_username) { + console.log("Ban request recieved. Target " + target_user); + if (this.ownerId === ownerId) { + console.log("Checked, owner sent ban"); + this.emitMessage({ type: 'verified-ban', target: target_user, targetUsername: target_username}); + this.bannedUserList.push(target_user); + } + } owner_id (id) { console.log("Recieved a owner-id request"); From fffe6d11eb588a34ee27c56ffc54cf2124c57731 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 4 Nov 2024 17:52:43 -0600 Subject: [PATCH 04/16] removal of console logging --- client/multiplayer/room.jsx | 7 ------- client/multiplayer/room.min.js | 2 +- client/scripts/upsertPlayerItem.js | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index ae47e693c..072aa9e21 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -241,9 +241,6 @@ function connectionAcknowledged ({ } async function processPlayers(messagePlayers) { const owner_id = await get_owner_id(); - - console.log("Await done"); - await Promise.all(Object.keys(messagePlayers).map(async (userId) => { messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; players[userId] = messagePlayers[userId]; @@ -535,9 +532,7 @@ function noQuestionsFound () { let resolveOwnerId; function ownerCheck({ id }) { - console.log("Owner check received at base at set ownerId"); ownerId = id; - console.log("Owner ID found to be " + ownerId); // Resolve the Promise to indicate that ownerId is set if (resolveOwnerId) { @@ -550,9 +545,7 @@ function get_owner_id() { return new Promise((resolve) => { resolveOwnerId = resolve; socket.send(JSON.stringify({ type: 'owner-id' })); - console.log("Prepping send"); }).then(() => { - console.log("All good! then received, returning owner ID at " + ownerId); return ownerId; }); } diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 6e2d1e022..776f6f49c 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,5 @@ import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24;/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"enforcing-ban":return ackBannedFromRoom();case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await get_owner_id();console.log("Await done"),await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"})),console.log("Prepping send")}).then(()=>(console.log("All good! then received, returning owner ID at "+ownerId),ownerId))}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"enforcing-ban":return ackBannedFromRoom();case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await get_owner_id();console.log("Await done"),await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}).then(()=>ownerId)}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,01>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index 13c91f4c9..6f7d2132f 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -75,7 +75,6 @@ export default function upsertPlayerItem(player, USER_ID, ownerId, socket) { banButton.addEventListener('click', () => { - console.log(`Banning user ${username} (ID: ${userId})`); socket.send(JSON.stringify({ type: 'ban', ownerId: ownerId, target_user: userId, targ_name: username })); }); } From ae698132f052bb9b06e126fd3a820edfc019abb9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 4 Nov 2024 18:08:36 -0600 Subject: [PATCH 05/16] linted according to semistandard --- client/multiplayer/room.jsx | 44 +++++------- client/multiplayer/room.min.js | 2 +- client/scripts/upsertPlayerItem.js | 98 ++++++++++++-------------- quizbowl/Player.js | 5 +- routes/api/query.js | 2 +- server/multiplayer/ServerTossupRoom.js | 29 ++++---- 6 files changed, 85 insertions(+), 95 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index 072aa9e21..a9d393837 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -11,7 +11,7 @@ import upsertPlayerItem from '../scripts/upsertPlayerItem.js'; const categoryManager = new CategoryManager(); let oldCategories = JSON.stringify(categoryManager.export()); let startingDifficulties = []; -let ownerId = "" +let ownerId = ''; let maxPacketNumber = 24; /** @@ -94,26 +94,23 @@ socket.onmessage = function (event) { case 'verified-ban': return recvBan(data); } }; -function ackBannedFromRoom() { - window.alert("You were banned from this room by the room owner, and cannot rejoin it."); +function ackBannedFromRoom () { + window.alert('You were banned from this room by the room owner, and cannot rejoin it.'); setTimeout(() => { - window.location.replace("../"); - }, 100); - + window.location.replace('../'); + }, 100); } -function recvBan({target, targetUsername}) { +function recvBan ({ target, targetUsername }) { if (target === USER_ID) { - window.alert("You were banned from this room by the room owner."); + window.alert('You were banned from this room by the room owner.'); setTimeout(() => { - window.location.replace("../"); - }, 100); + window.location.replace('../'); + }, 100); } else { - logEvent(targetUsername + " has been banned from this room."); + logEvent(targetUsername + ' has been banned from this room.'); } - } - function buzz ({ userId, username }) { logEvent(username, 'buzzed'); document.getElementById('buzz').disabled = true; @@ -239,17 +236,16 @@ function connectionAcknowledged ({ USER_ID = userId; window.localStorage.setItem('USER_ID', USER_ID); } -async function processPlayers(messagePlayers) { - const owner_id = await get_owner_id(); +async function processPlayers (messagePlayers) { + const recvOwnerId = await getRecvOwnerId(); await Promise.all(Object.keys(messagePlayers).map(async (userId) => { messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; players[userId] = messagePlayers[userId]; - - upsertPlayerItem(players[userId], USER_ID, owner_id, socket); + + upsertPlayerItem(players[userId], USER_ID, recvOwnerId, socket); })); } - async function connectionAcknowledgedQuery ({ difficulties = [], minYear, @@ -531,9 +527,9 @@ function noQuestionsFound () { let resolveOwnerId; -function ownerCheck({ id }) { +function ownerCheck ({ id }) { ownerId = id; - + // Resolve the Promise to indicate that ownerId is set if (resolveOwnerId) { resolveOwnerId(); @@ -541,13 +537,12 @@ function ownerCheck({ id }) { } } -function get_owner_id() { - return new Promise((resolve) => { +async function getRecvOwnerId () { + await new Promise((resolve) => { resolveOwnerId = resolve; socket.send(JSON.stringify({ type: 'owner-id' })); - }).then(() => { - return ownerId; }); + return ownerId; } function pause ({ paused, username }) { @@ -648,7 +643,6 @@ function setUsername ({ oldUsername, newUsername, userId }) { document.getElementById('username-' + userId).textContent = newUsername; players[userId].username = newUsername; sortPlayerListGroup(); - if (userId === USER_ID) { username = newUsername; diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 776f6f49c..1a55c783f 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,5 @@ import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24;/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"enforcing-ban":return ackBannedFromRoom();case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await get_owner_id();console.log("Await done"),await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}).then(()=>ownerId)}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"enforcing-ban":return ackBannedFromRoom();case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,01>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index 6f7d2132f..704f2d35d 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -6,51 +6,49 @@ import { escapeHTML } from './utilities/strings.js'; * @param {string} USER_ID - The item is highlighted blue if `USER_ID === player.userId`. * @param {string} ownerId - ID of the room owner */ -export default function upsertPlayerItem(player, USER_ID, ownerId, socket) { - if (!player || !player.userId) { - console.error("Player or player.userId is undefined", { player }); - return; - } +export default function upsertPlayerItem (player, USER_ID, ownerId, socket) { + if (!player || !player.userId) { + console.error('Player or player.userId is undefined', { player }); + return; + } - const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; - const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; - - const playerIsOwner = ownerId === userId; - const iAmOwner = ownerId === USER_ID; - const res = playerIsOwner ? "Yes" : "No"; + const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; + const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; - // Remove the existing player item if it exists - if (document.getElementById('list-group-' + userId)) { - document.getElementById('list-group-' + userId).remove(); - } + const playerIsOwner = ownerId === userId; + const iAmOwner = ownerId === USER_ID; + const res = playerIsOwner ? 'Yes' : 'No'; - - const playerItem = document.createElement('a'); - playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; - playerItem.id = `list-group-${userId}`; - - - const displayUsername = playerIsOwner ? `👑 ${escapeHTML(username)}` : escapeHTML(username); + // Remove the existing player item if it exists + if (document.getElementById('list-group-' + userId)) { + document.getElementById('list-group-' + userId).remove(); + } - playerItem.innerHTML = ` + const playerItem = document.createElement('a'); + playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; + playerItem.id = `list-group-${userId}`; + + const displayUsername = playerIsOwner ? `👑 ${escapeHTML(username)}` : escapeHTML(username); + + playerItem.innerHTML = `
    ${displayUsername} ${points}
    `; - // Set attributes for the popover - playerItem.setAttribute('data-bs-container', 'body'); - playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); - playerItem.setAttribute('data-bs-html', 'true'); - playerItem.setAttribute('data-bs-placement', 'left'); - playerItem.setAttribute('data-bs-toggle', 'popover'); - playerItem.setAttribute('data-bs-trigger', 'focus'); - playerItem.setAttribute('tabindex', '0'); + // Set attributes for the popover + playerItem.setAttribute('data-bs-container', 'body'); + playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); + playerItem.setAttribute('data-bs-html', 'true'); + playerItem.setAttribute('data-bs-placement', 'left'); + playerItem.setAttribute('data-bs-toggle', 'popover'); + playerItem.setAttribute('data-bs-trigger', 'focus'); + playerItem.setAttribute('tabindex', '0'); - // Popover content - playerItem.setAttribute('data-bs-title', username); - playerItem.setAttribute('data-bs-content', ` + // Popover content + playerItem.setAttribute('data-bs-title', username); + playerItem.setAttribute('data-bs-content', `
    • Powers${powers}
    • Tens${tens}
    • @@ -61,24 +59,22 @@ export default function upsertPlayerItem(player, USER_ID, ownerId, socket) {
    `); - - document.getElementById('player-list-group').appendChild(playerItem); + document.getElementById('player-list-group').appendChild(playerItem); + + // ban button if the viewer is the owner and the player is not + if (iAmOwner && userId !== ownerId) { + const banButton = document.createElement('button'); + banButton.className = 'btn btn-danger btn-sm mt-2'; + banButton.innerText = 'Ban'; - //ban button if the viewer is the owner and the player is not - if (iAmOwner && userId !== ownerId) { - const banButton = document.createElement('button'); - banButton.className = 'btn btn-danger btn-sm mt-2'; - banButton.innerText = 'Ban'; - - - playerItem.appendChild(banButton); + playerItem.appendChild(banButton); - - banButton.addEventListener('click', () => { - socket.send(JSON.stringify({ type: 'ban', ownerId: ownerId, target_user: userId, targ_name: username })); - }); - } + banButton.addEventListener('click', () => { + socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); + }); + } - // popover - new bootstrap.Popover(playerItem); + // bootstrap requires "new" to be called on each popover + // eslint-disable-next-line no-new + new bootstrap.Popover(playerItem); } diff --git a/quizbowl/Player.js b/quizbowl/Player.js index 2b3c26dc0..ffdfab77c 100644 --- a/quizbowl/Player.js +++ b/quizbowl/Player.js @@ -22,8 +22,9 @@ class Player { } }; } - assignOwner() { - this.owner = true + + assignOwner () { + this.owner = true; } clearStats () { diff --git a/routes/api/query.js b/routes/api/query.js index c41de3c33..7b3df20bc 100644 --- a/routes/api/query.js +++ b/routes/api/query.js @@ -67,7 +67,7 @@ router.get('/', async (req, res) => { try { const queryResult = await getQuery(req.query); - res.json(queryResult); + res.json(queryResult); } catch (error) { switch (error.message) { case 'Invalid question type specified.': diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 1d2d923ad..6e4145cb8 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -15,7 +15,7 @@ import checkAnswer from 'qb-answer-checker'; export default class ServerTossupRoom extends TossupRoom { constructor (name, ownerId, isPermanent = false, categories = [], subcategories = [], alternateSubcategories = []) { super(name, categories, subcategories, alternateSubcategories); - this.ownerId = ownerId + this.ownerId = ownerId; this.isPermanent = isPermanent; this.checkAnswer = checkAnswer; this.getNumPackets = getNumPackets; @@ -45,14 +45,12 @@ export default class ServerTossupRoom extends TossupRoom { case 'toggle-lock': return this.toggleLock(userId, message); case 'toggle-login-required': return this.toggleLoginRequired(userId, message); case 'toggle-public': return this.togglePublic(userId, message); - case 'owner-id': return this.owner_id(this.ownerId) + case 'owner-id': return this.owner_id(this.ownerId); default: super.message(userId, message); } } connection (socket, userId, username) { - - console.log(`Connection in room ${HEADER}${this.name}${ENDC} - ID of owner: ${OKBLUE}${this.ownerId}${ENDC} - userId: ${OKBLUE}${userId}${ENDC}, username: ${OKBLUE}${username}${ENDC} - with settings ${OKGREEN}${Object.keys(this.settings).map(key => [key, this.settings[key]].join(': ')).join('; ')};${ENDC}`); const isNew = !(userId in this.players); @@ -61,10 +59,10 @@ export default class ServerTossupRoom extends TossupRoom { this.sockets[userId] = socket; username = this.players[userId].safelySetUsername(username); if (this.bannedUserList.includes(userId)) { - console.log("Banned user " + userId + " (" + username + ") tried to join a room"); - this.sendToSocket(userId, {type: 'enforcing-ban'}) + console.log('Banned user ' + userId + ' (' + username + ') tried to join a room'); + this.sendToSocket(userId, { type: 'enforcing-ban' }); return; - } + } socket.on('message', message => { if (this.rateLimiter(socket) && !this.rateLimitExceeded.has(username)) { @@ -83,7 +81,6 @@ export default class ServerTossupRoom extends TossupRoom { }); socket.on('close', this.close.bind(this, userId)); - socket.send(JSON.stringify({ type: 'connection-acknowledged', @@ -119,20 +116,22 @@ export default class ServerTossupRoom extends TossupRoom { this.emitMessage({ type: 'join', isNew, userId, username, user: this.players[userId] }); } - banUser(ownerId, target_user, target_username) { - console.log("Ban request recieved. Target " + target_user); + + banUser (ownerId, targetUser, targetUsername) { + console.log('Ban request recieved. Target ' + targetUser); if (this.ownerId === ownerId) { - console.log("Checked, owner sent ban"); - this.emitMessage({ type: 'verified-ban', target: target_user, targetUsername: target_username}); - this.bannedUserList.push(target_user); + console.log('Checked, owner sent ban'); + this.emitMessage({ type: 'verified-ban', target: targetUser, targetUsername }); + this.bannedUserList.push(targetUser); } } owner_id (id) { - console.log("Recieved a owner-id request"); + console.log('Recieved a owner-id request'); this.emitMessage({ type: 'owner-check', id }); - console.log("Owner check sent"); + console.log('Owner check sent'); } + chat (userId, { message }) { // prevent chat messages if room is public, since they can still be sent with API if (this.settings.public || typeof message !== 'string') { return false; } From 83c1e21b6921233e794bb2fc87ac29b78a796caa Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 4 Nov 2024 22:23:59 -0600 Subject: [PATCH 06/16] basic votekick made, maybe buggy --- client/multiplayer/room.jsx | 43 +++++++-- client/multiplayer/room.min.js | 6 +- client/scripts/upsertPlayerItem.js | 127 ++++++++++++++++--------- server/multiplayer/ServerTossupRoom.js | 99 ++++++++++++++++++- 4 files changed, 217 insertions(+), 58 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index a9d393837..d4bbcd83f 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -13,7 +13,7 @@ let oldCategories = JSON.stringify(categoryManager.export()); let startingDifficulties = []; let ownerId = ''; let maxPacketNumber = 24; - +let globalPublic = true; /** * userId to player object */ @@ -50,6 +50,7 @@ socket.onmessage = function (event) { const data = JSON.parse(event.data); switch (data.type) { case 'enforcing-ban': return ackBannedFromRoom(); + case 'enforcing-kick': return ackKickedFromRoom(); case 'buzz': return buzz(data); case 'force-username': return forceUsername(data); case 'chat': return chat(data, false); @@ -92,8 +93,30 @@ socket.onmessage = function (event) { case 'toggle-timer': return toggleTimer(data); case 'update-question': return updateQuestion(data); case 'verified-ban': return recvBan(data); + case 'successful-vk': return vkHandle(data); + case 'initiated-vk': return vkInit(data); } }; +function vkInit({ targetName, targetId, threshold }) { + logEvent("A votekick has been started against user " + targetName + " and needs " + threshold + " votes to suceed.") +} +function vkHandle({targetName, targetId}) { + if (USER_ID === targetId) { + window.alert('You were vote kicked from this room by others.'); + setTimeout(() => { + window.location.replace('../'); + }, 100); + } else { + logEvent(targetName + " has been vote kicked from this room.") + } +} +function ackKickedFromRoom() { + window.alert('You were kicked from this room by players, and cannot rejoin it.'); + setTimeout(() => { + window.location.replace('../'); + }, 100); +} + function ackBannedFromRoom () { window.alert('You were banned from this room by the room owner, and cannot rejoin it.'); setTimeout(() => { @@ -160,7 +183,7 @@ function clearStats ({ userId }) { for (const field of ['celerity', 'negs', 'points', 'powers', 'tens', 'tuh', 'zeroes']) { players[userId][field] = 0; } - upsertPlayerItem(players[userId], USER_ID, ownerId, socket); + upsertPlayerItem(players[userId], USER_ID, ownerId, socket, globalPublic); sortPlayerListGroup(); } @@ -220,6 +243,7 @@ function connectionAcknowledged ({ document.getElementById('toggle-login-required').disabled = settings.public; document.getElementById('toggle-timer').disabled = settings.public; document.getElementById('toggle-public').checked = settings.public; + globalPublic = settings.public; document.getElementById('reading-speed').value = settings.readingSpeed; document.getElementById('reading-speed-display').textContent = settings.readingSpeed; @@ -242,7 +266,7 @@ async function processPlayers (messagePlayers) { messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; players[userId] = messagePlayers[userId]; - upsertPlayerItem(players[userId], USER_ID, recvOwnerId, socket); + upsertPlayerItem(players[userId], USER_ID, recvOwnerId, socket, globalPublic); })); } @@ -352,7 +376,7 @@ async function giveAnswer ({ celerity, directive, directedPrompt, givenAnswer, p players[userId].tuh++; players[userId].celerity = celerity; - upsertPlayerItem(players[userId], USER_ID, ownerId, socket); + upsertPlayerItem(players[userId], USER_ID, ownerId, socket, globalPublic); sortPlayerListGroup(); } @@ -383,7 +407,7 @@ function join ({ isNew, user, userId, username }) { if (isNew) { user.celerity = user.celerity.correct.average; - upsertPlayerItem(user, USER_ID, ownerId, socket); + upsertPlayerItem(user, USER_ID, ownerId, socket, globalPublic); sortPlayerListGroup(); players[userId] = user; } else { @@ -649,7 +673,7 @@ function setUsername ({ oldUsername, newUsername, userId }) { window.localStorage.setItem('multiplayer-username', username); document.getElementById('username').value = username; } - upsertPlayerItem(players[userId], USER_ID, ownerId, socket); + upsertPlayerItem(players[userId], USER_ID, ownerId, socket, globalPublic); } function toggleLock ({ lock, username }) { @@ -713,11 +737,16 @@ function togglePublic ({ public: isPublic, username }) { document.getElementById('toggle-timer').disabled = isPublic; document.getElementById('toggle-timer').checked = true; document.getElementById('toggle-public').checked = isPublic; - + globalPublic = isPublic; if (isPublic) { document.getElementById('toggle-lock').checked = false; document.getElementById('toggle-login-required').checked = false; } + Object.keys(players).forEach((player) => { + console.log(player); + upsertPlayerItem(players[player], USER_ID, ownerId, socket, globalPublic); + }) + } function updateQuestion ({ word }) { diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 1a55c783f..4e6cf35cb 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,5 @@ -import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24;/** +import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24,globalPublic=!0;/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"enforcing-ban":return ackBannedFromRoom();case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username -if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,01>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"enforcing-ban":return ackBannedFromRoom();case"enforcing-kick":return ackKickedFromRoom();case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b);case"successful-vk":return vkHandle(b);case"initiated-vk":return vkInit(b)}};function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}function ackKickedFromRoom(){window.alert("You were kicked from this room by players, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{console.log(a),upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function setYearRange({minYear:a,maxYear:b,username:c}){c&&logEvent(c,`changed the year range to ${a}-${b}`),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index 704f2d35d..bf4c5f31f 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -6,49 +6,49 @@ import { escapeHTML } from './utilities/strings.js'; * @param {string} USER_ID - The item is highlighted blue if `USER_ID === player.userId`. * @param {string} ownerId - ID of the room owner */ -export default function upsertPlayerItem (player, USER_ID, ownerId, socket) { - if (!player || !player.userId) { - console.error('Player or player.userId is undefined', { player }); - return; - } +export default function upsertPlayerItem(player, USER_ID, ownerId, socket, isPublic) { + if (!player || !player.userId) { + console.error('Player or player.userId is undefined', { player }); + return; + } - const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; - const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; + const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; + const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; - const playerIsOwner = ownerId === userId; - const iAmOwner = ownerId === USER_ID; - const res = playerIsOwner ? 'Yes' : 'No'; + const playerIsOwner = ownerId === userId; + const iAmOwner = ownerId === USER_ID; + const res = playerIsOwner ? 'Yes' : 'No'; - // Remove the existing player item if it exists - if (document.getElementById('list-group-' + userId)) { - document.getElementById('list-group-' + userId).remove(); - } + // Remove the existing player item if it exists + if (document.getElementById('list-group-' + userId)) { + document.getElementById('list-group-' + userId).remove(); + } - const playerItem = document.createElement('a'); - playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; - playerItem.id = `list-group-${userId}`; + const playerItem = document.createElement('a'); + playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; + playerItem.id = `list-group-${userId}`; + console.log("Is it public? " + isPublic); + const displayUsername = (playerIsOwner && !isPublic) ? `👑 ${escapeHTML(username)}` : escapeHTML(username); - const displayUsername = playerIsOwner ? `👑 ${escapeHTML(username)}` : escapeHTML(username); - - playerItem.innerHTML = ` + playerItem.innerHTML = `
    ${displayUsername} ${points}
    `; - // Set attributes for the popover - playerItem.setAttribute('data-bs-container', 'body'); - playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); - playerItem.setAttribute('data-bs-html', 'true'); - playerItem.setAttribute('data-bs-placement', 'left'); - playerItem.setAttribute('data-bs-toggle', 'popover'); - playerItem.setAttribute('data-bs-trigger', 'focus'); - playerItem.setAttribute('tabindex', '0'); - - // Popover content - playerItem.setAttribute('data-bs-title', username); - playerItem.setAttribute('data-bs-content', ` + // Set attributes for the popover + playerItem.setAttribute('data-bs-container', 'body'); + playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); + playerItem.setAttribute('data-bs-html', 'true'); + playerItem.setAttribute('data-bs-placement', 'left'); + playerItem.setAttribute('data-bs-toggle', 'popover'); + playerItem.setAttribute('data-bs-trigger', 'focus'); + playerItem.setAttribute('tabindex', '0'); + + // Popover content + playerItem.setAttribute('data-bs-title', username); + playerItem.setAttribute('data-bs-content', `
    • Powers${powers}
    • Tens${tens}
    • @@ -59,22 +59,57 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket) {
    `); - document.getElementById('player-list-group').appendChild(playerItem); + document.getElementById('player-list-group').appendChild(playerItem); + + // ban button if the viewer is the owner and the player is not, also room has to be private + if (iAmOwner && userId !== ownerId && !isPublic) { + const banButton = document.createElement('button'); + banButton.className = 'btn btn-danger btn-sm mt-2'; + banButton.title = 'Ban an user. They can no longer join the room.' + banButton.innerText = 'Ban'; + + playerItem.appendChild(banButton); + + banButton.addEventListener('click', () => { + socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); + }); + } + let conditionalVK; + if (!isPublic) { + if (ownerId === userId) { + conditionalVK = false; + } else { + conditionalVK = true; + } + + } else { + conditionalVK = true; + } + + if (userId !== USER_ID && conditionalVK) { + const vkButton = document.createElement('button'); + vkButton.className = 'btn btn-warning btn-sm mt-2'; + vkButton.title = 'Initiate a votekick on an user. 90 second cooldown.' + vkButton.innerText = 'VK'; + + playerItem.appendChild(vkButton); + + vkButton.addEventListener('click', () => { + socket.send(JSON.stringify({ type: 'votekick-vote', target_user: userId, targ_name: username, send_id: USER_ID })); + socket.send(JSON.stringify({ type: 'votekick-init', target_user: userId, targ_name: username, send_id: USER_ID })); - // ban button if the viewer is the owner and the player is not - if (iAmOwner && userId !== ownerId) { - const banButton = document.createElement('button'); - banButton.className = 'btn btn-danger btn-sm mt-2'; - banButton.innerText = 'Ban'; + vkButton.disabled = true; + vkButton.innerText = 'Cooldown'; - playerItem.appendChild(banButton); + setTimeout(() => { + vkButton.disabled = false; + vkButton.innerText = 'VK'; + }, 90000); + }); + } - banButton.addEventListener('click', () => { - socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); - }); - } - // bootstrap requires "new" to be called on each popover - // eslint-disable-next-line no-new - new bootstrap.Popover(playerItem); + // bootstrap requires "new" to be called on each popover + // eslint-disable-next-line no-new + new bootstrap.Popover(playerItem); } diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 6e4145cb8..4df7daa5d 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -12,6 +12,34 @@ import getNumPackets from '../../database/qbreader/get-num-packets.js'; import checkAnswer from 'qb-answer-checker'; +class Votekick { + constructor(targName, targId, voted = [], threshold) { + this.targName = targName; + this.targId = targId; + this.voted = Array.isArray(voted) ? voted : []; + this.threshold = threshold; + } + exists(givenId) { + if (this.targId === givenId) { + return true; + } else { + return false; + } + } + vote(votingId) { + if (!this.voted.includes(votingId)) { + this.voted.push(votingId); + } + } + check() { + if (this.voted.length >= this.threshold) { + return true; + } else { + return false; + } + } + +} export default class ServerTossupRoom extends TossupRoom { constructor (name, ownerId, isPermanent = false, categories = [], subcategories = [], alternateSubcategories = []) { super(name, categories, subcategories, alternateSubcategories); @@ -23,7 +51,9 @@ export default class ServerTossupRoom extends TossupRoom { this.getSet = getSet; this.getSetList = getSetList; this.bannedUserList = []; - + this.kickedUserList = []; + this.votekickList = []; + this.rateLimiter = new RateLimit(50, 1000); this.rateLimitExceeded = new Set(); this.settings = { @@ -36,7 +66,8 @@ export default class ServerTossupRoom extends TossupRoom { this.getSetList().then(setList => { this.setList = setList; }); } - async message (userId, message) { + async message(userId, message) { + switch (message.type) { case 'ban': return this.banUser(message.ownerId, message.target_user, message.targ_name); case 'chat': return this.chat(userId, message); @@ -46,10 +77,69 @@ export default class ServerTossupRoom extends TossupRoom { case 'toggle-login-required': return this.toggleLoginRequired(userId, message); case 'toggle-public': return this.togglePublic(userId, message); case 'owner-id': return this.owner_id(this.ownerId); + case 'votekick-init': return this.votekickInit(message.target_user, message.targ_name, message.send_id); + case 'votekick-vote': return this.votekickVote(message.target_user, message.send_id); default: super.message(userId, message); } } + votekickInit(targetId, targetName, sendingId) { + if (!this.lastVotekickTime) { + this.lastVotekickTime = {}; + } + + const currentTime = Date.now(); + if (this.lastVotekickTime[sendingId] && (currentTime - this.lastVotekickTime[sendingId] < 90000)) { + console.log(`Votekick denied: ${sendingId} 90 second cooldown viol.`); + return; + } + + this.lastVotekickTime[sendingId] = currentTime; + + let exists = false; + this.votekickList.forEach((votekick) => { + if (votekick.exists(targetId)) { + exists = true; + } + }); + if (exists) { + return; + } + + let threshold = Math.max((Object.keys(this.players).length) - 1, 0); + let votekick = new Votekick(targetName, targetId, [], threshold); + votekick.vote(sendingId); + if (votekick.check()) { + this.emitMessage({ type: 'successful-vk', targetName: votekick.targName, targetId: votekick.targId }); + this.kickedUserList.push(targetId); + } else { + this.votekickList.push(votekick); + this.emitMessage({ type: 'initiated-vk', targetName: votekick.targName, targetId: votekick.targId, threshold }); + } + console.log(this.votekickList); + } + + votekickVote(targetUser, votingId) { + let exists = false; + let thisVotekick; + this.votekickList.forEach((votekick) => { + if (votekick.exists(targetUser)) { + thisVotekick = votekick; + exists = true; + } + }) + if (!exists) { + return; + } + thisVotekick.vote(votingId); + if (thisVotekick.check()) { + this.emitMessage({ type: 'successful-vk', targetName: thisVotekick.targName, targetId: thisVotekick.targId }) + this.kickedUserList.push(targetUser); + } + console.log(this.votekickList); + + } + connection (socket, userId, username) { console.log(`Connection in room ${HEADER}${this.name}${ENDC} - ID of owner: ${OKBLUE}${this.ownerId}${ENDC} - userId: ${OKBLUE}${userId}${ENDC}, username: ${OKBLUE}${username}${ENDC} - with settings ${OKGREEN}${Object.keys(this.settings).map(key => [key, this.settings[key]].join(': ')).join('; ')};${ENDC}`); @@ -63,6 +153,11 @@ export default class ServerTossupRoom extends TossupRoom { this.sendToSocket(userId, { type: 'enforcing-ban' }); return; } + if (this.kickedUserList.includes(userId)) { + console.log('Kicked user ' + userId + ' (' + username + ') tried to join a room'); + this.sendToSocket(userId, { type: 'enforcing-kick' }); + return; + } socket.on('message', message => { if (this.rateLimiter(socket) && !this.rateLimitExceeded.has(username)) { From 25b5ca6dd9ce2f3825a7faeaa2c99bf7658afab8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 4 Nov 2024 22:29:42 -0600 Subject: [PATCH 07/16] cleaned up + linted --- client/multiplayer/room.jsx | 23 ++-- client/multiplayer/room.min.js | 2 +- client/scripts/upsertPlayerItem.js | 162 ++++++++++++------------- quizbowl/TossupRoom.js | 7 +- server/multiplayer/ServerTossupRoom.js | 30 ++--- 5 files changed, 109 insertions(+), 115 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index d4bbcd83f..ee0f1dc4e 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -49,20 +49,21 @@ socket.onclose = function (event) { socket.onmessage = function (event) { const data = JSON.parse(event.data); switch (data.type) { - case 'enforcing-ban': return ackBannedFromRoom(); - case 'enforcing-kick': return ackKickedFromRoom(); case 'buzz': return buzz(data); - case 'force-username': return forceUsername(data); case 'chat': return chat(data, false); case 'chat-live-update': return chat(data, true); case 'clear-stats': return clearStats(data); case 'connection-acknowledged': return connectionAcknowledged(data); case 'connection-acknowledged-query': return connectionAcknowledgedQuery(data); case 'connection-acknowledged-tossup': return connectionAcknowledgedTossup(data); + case 'enforcing-ban': return ackBannedFromRoom(); + case 'enforcing-kick': return ackKickedFromRoom(); case 'end-of-set': return endOfSet(data); case 'error': return handleError(data); + case 'force-username': return forceUsername(data); case 'give-answer': return giveAnswer(data); case 'give-answer-live-update': return logGiveAnswer(data, true); + case 'initiated-vk': return vkInit(data); case 'join': return join(data); case 'leave': return leave(data); case 'lost-buzzer-race': return lostBuzzerRace(data); @@ -81,6 +82,7 @@ socket.onmessage = function (event) { case 'set-year-range': return setYearRange(data); case 'skip': return next(data); case 'start': return next(data); + case 'successful-vk': return vkHandle(data); case 'timer-update': return updateTimerDisplay(data.timeRemaining); case 'toggle-lock': return toggleLock(data); case 'toggle-login-required': return toggleLoginRequired(data); @@ -93,24 +95,22 @@ socket.onmessage = function (event) { case 'toggle-timer': return toggleTimer(data); case 'update-question': return updateQuestion(data); case 'verified-ban': return recvBan(data); - case 'successful-vk': return vkHandle(data); - case 'initiated-vk': return vkInit(data); } }; -function vkInit({ targetName, targetId, threshold }) { - logEvent("A votekick has been started against user " + targetName + " and needs " + threshold + " votes to suceed.") +function vkInit ({ targetName, targetId, threshold }) { + logEvent('A votekick has been started against user ' + targetName + ' and needs ' + threshold + ' votes to suceed.'); } -function vkHandle({targetName, targetId}) { +function vkHandle ({ targetName, targetId }) { if (USER_ID === targetId) { window.alert('You were vote kicked from this room by others.'); setTimeout(() => { window.location.replace('../'); }, 100); } else { - logEvent(targetName + " has been vote kicked from this room.") + logEvent(targetName + ' has been vote kicked from this room.'); } } -function ackKickedFromRoom() { +function ackKickedFromRoom () { window.alert('You were kicked from this room by players, and cannot rejoin it.'); setTimeout(() => { window.location.replace('../'); @@ -745,8 +745,7 @@ function togglePublic ({ public: isPublic, username }) { Object.keys(players).forEach((player) => { console.log(player); upsertPlayerItem(players[player], USER_ID, ownerId, socket, globalPublic); - }) - + }); } function updateQuestion ({ word }) { diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 4e6cf35cb..076377028 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,5 @@ import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24,globalPublic=!0;/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"enforcing-ban":return ackBannedFromRoom();case"enforcing-kick":return ackKickedFromRoom();case"buzz":return buzz(b);case"force-username":return forceUsername(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b);case"successful-vk":return vkHandle(b);case"initiated-vk":return vkInit(b)}};function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}function ackKickedFromRoom(){window.alert("You were kicked from this room by players, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"enforcing-ban":return ackBannedFromRoom();case"enforcing-kick":return ackKickedFromRoom();case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"force-username":return forceUsername(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"initiated-vk":return vkInit(b);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"successful-vk":return vkHandle(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}function ackKickedFromRoom(){window.alert("You were kicked from this room by players, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{console.log(a),upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function setYearRange({minYear:a,maxYear:b,username:c}){c&&logEvent(c,`changed the year range to ${a}-${b}`),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index bf4c5f31f..cfee6a8be 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -6,49 +6,49 @@ import { escapeHTML } from './utilities/strings.js'; * @param {string} USER_ID - The item is highlighted blue if `USER_ID === player.userId`. * @param {string} ownerId - ID of the room owner */ -export default function upsertPlayerItem(player, USER_ID, ownerId, socket, isPublic) { - if (!player || !player.userId) { - console.error('Player or player.userId is undefined', { player }); - return; - } - - const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; - const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; - - const playerIsOwner = ownerId === userId; - const iAmOwner = ownerId === USER_ID; - const res = playerIsOwner ? 'Yes' : 'No'; - - // Remove the existing player item if it exists - if (document.getElementById('list-group-' + userId)) { - document.getElementById('list-group-' + userId).remove(); - } - - const playerItem = document.createElement('a'); - playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; - playerItem.id = `list-group-${userId}`; - console.log("Is it public? " + isPublic); - const displayUsername = (playerIsOwner && !isPublic) ? `👑 ${escapeHTML(username)}` : escapeHTML(username); - - playerItem.innerHTML = ` +export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPublic) { + if (!player || !player.userId) { + console.error('Player or player.userId is undefined', { player }); + return; + } + + const { userId, username, powers = 0, tens = 0, negs = 0, tuh = 0, points = 0, online } = player; + const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; + + const playerIsOwner = ownerId === userId; + const iAmOwner = ownerId === USER_ID; + const res = playerIsOwner ? 'Yes' : 'No'; + + // Remove the existing player item if it exists + if (document.getElementById('list-group-' + userId)) { + document.getElementById('list-group-' + userId).remove(); + } + + const playerItem = document.createElement('a'); + playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; + playerItem.id = `list-group-${userId}`; + console.log('Is it public? ' + isPublic); + const displayUsername = (playerIsOwner && !isPublic) ? `👑 ${escapeHTML(username)}` : escapeHTML(username); + + playerItem.innerHTML = `
    ${displayUsername} ${points}
    `; - // Set attributes for the popover - playerItem.setAttribute('data-bs-container', 'body'); - playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); - playerItem.setAttribute('data-bs-html', 'true'); - playerItem.setAttribute('data-bs-placement', 'left'); - playerItem.setAttribute('data-bs-toggle', 'popover'); - playerItem.setAttribute('data-bs-trigger', 'focus'); - playerItem.setAttribute('tabindex', '0'); - - // Popover content - playerItem.setAttribute('data-bs-title', username); - playerItem.setAttribute('data-bs-content', ` + // Set attributes for the popover + playerItem.setAttribute('data-bs-container', 'body'); + playerItem.setAttribute('data-bs-custom-class', 'custom-popover'); + playerItem.setAttribute('data-bs-html', 'true'); + playerItem.setAttribute('data-bs-placement', 'left'); + playerItem.setAttribute('data-bs-toggle', 'popover'); + playerItem.setAttribute('data-bs-trigger', 'focus'); + playerItem.setAttribute('tabindex', '0'); + + // Popover content + playerItem.setAttribute('data-bs-title', username); + playerItem.setAttribute('data-bs-content', `
    • Powers${powers}
    • Tens${tens}
    • @@ -59,57 +59,55 @@ export default function upsertPlayerItem(player, USER_ID, ownerId, socket, isPub
    `); - document.getElementById('player-list-group').appendChild(playerItem); - - // ban button if the viewer is the owner and the player is not, also room has to be private - if (iAmOwner && userId !== ownerId && !isPublic) { - const banButton = document.createElement('button'); - banButton.className = 'btn btn-danger btn-sm mt-2'; - banButton.title = 'Ban an user. They can no longer join the room.' - banButton.innerText = 'Ban'; - - playerItem.appendChild(banButton); - - banButton.addEventListener('click', () => { - socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); - }); - } - let conditionalVK; - if (!isPublic) { - if (ownerId === userId) { - conditionalVK = false; - } else { - conditionalVK = true; - } - + document.getElementById('player-list-group').appendChild(playerItem); + + // ban button if the viewer is the owner and the player is not, also room has to be private + if (iAmOwner && userId !== ownerId && !isPublic) { + const banButton = document.createElement('button'); + banButton.className = 'btn btn-danger btn-sm mt-2'; + banButton.title = 'Ban an user. They can no longer join the room.'; + banButton.innerText = 'Ban'; + + playerItem.appendChild(banButton); + + banButton.addEventListener('click', () => { + socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); + }); + } + let conditionalVK; + if (!isPublic) { + if (ownerId === userId) { + conditionalVK = false; } else { - conditionalVK = true; + conditionalVK = true; } + } else { + conditionalVK = true; + } - if (userId !== USER_ID && conditionalVK) { - const vkButton = document.createElement('button'); - vkButton.className = 'btn btn-warning btn-sm mt-2'; - vkButton.title = 'Initiate a votekick on an user. 90 second cooldown.' - vkButton.innerText = 'VK'; - - playerItem.appendChild(vkButton); + if (userId !== USER_ID && conditionalVK) { + const vkButton = document.createElement('button'); + vkButton.className = 'btn btn-warning btn-sm mt-2'; + vkButton.title = 'Initiate a votekick on an user. 90 second cooldown.'; + vkButton.innerText = 'VK'; - vkButton.addEventListener('click', () => { - socket.send(JSON.stringify({ type: 'votekick-vote', target_user: userId, targ_name: username, send_id: USER_ID })); - socket.send(JSON.stringify({ type: 'votekick-init', target_user: userId, targ_name: username, send_id: USER_ID })); + playerItem.appendChild(vkButton); - vkButton.disabled = true; - vkButton.innerText = 'Cooldown'; + vkButton.addEventListener('click', () => { + socket.send(JSON.stringify({ type: 'votekick-vote', target_user: userId, targ_name: username, send_id: USER_ID })); + socket.send(JSON.stringify({ type: 'votekick-init', target_user: userId, targ_name: username, send_id: USER_ID })); - setTimeout(() => { - vkButton.disabled = false; - vkButton.innerText = 'VK'; - }, 90000); - }); - } + vkButton.disabled = true; + vkButton.innerText = 'Cooldown'; + setTimeout(() => { + vkButton.disabled = false; + vkButton.innerText = 'VK'; + }, 90000); + }); + } - // bootstrap requires "new" to be called on each popover - // eslint-disable-next-line no-new - new bootstrap.Popover(playerItem); + // bootstrap requires "new" to be called on each popover + // eslint-disable-next-line no-new + new bootstrap.Popover(playerItem); } diff --git a/quizbowl/TossupRoom.js b/quizbowl/TossupRoom.js index 2887969d7..d3c1029ee 100644 --- a/quizbowl/TossupRoom.js +++ b/quizbowl/TossupRoom.js @@ -85,12 +85,9 @@ export default class TossupRoom extends Room { case 'buzz': return this.buzz(userId, message); case 'clear-stats': return this.clearStats(userId, message); case 'give-answer': return this.giveAnswer(userId, message); - case 'next': case 'skip': - case 'start': - return this.next(userId, message); - + case 'start': return this.next(userId, message); case 'pause': return this.pause(userId, message); case 'set-categories': return this.setCategories(userId, message); case 'set-difficulties': return this.setDifficulties(userId, message); @@ -411,7 +408,7 @@ export default class TossupRoom extends Room { // calculate time needed before reading next word let time = Math.log(word.length) + 1; if ((word.endsWith('.') && word.charCodeAt(word.length - 2) > 96 && word.charCodeAt(word.length - 2) < 123) || - word.slice(-2) === '.\u201d' || word.slice(-2) === '!\u201d' || word.slice(-2) === '?\u201d') { + word.slice(-2) === '.\u201d' || word.slice(-2) === '!\u201d' || word.slice(-2) === '?\u201d') { time += 2; } else if (word.endsWith(',') || word.slice(-2) === ',\u201d') { time += 0.75; diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 4df7daa5d..691a49b8b 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -13,32 +13,34 @@ import getNumPackets from '../../database/qbreader/get-num-packets.js'; import checkAnswer from 'qb-answer-checker'; class Votekick { - constructor(targName, targId, voted = [], threshold) { + constructor (targName, targId, voted = [], threshold) { this.targName = targName; this.targId = targId; this.voted = Array.isArray(voted) ? voted : []; this.threshold = threshold; } - exists(givenId) { + + exists (givenId) { if (this.targId === givenId) { return true; } else { return false; } } - vote(votingId) { + + vote (votingId) { if (!this.voted.includes(votingId)) { this.voted.push(votingId); } } - check() { + + check () { if (this.voted.length >= this.threshold) { return true; } else { return false; } } - } export default class ServerTossupRoom extends TossupRoom { constructor (name, ownerId, isPermanent = false, categories = [], subcategories = [], alternateSubcategories = []) { @@ -53,7 +55,7 @@ export default class ServerTossupRoom extends TossupRoom { this.bannedUserList = []; this.kickedUserList = []; this.votekickList = []; - + this.rateLimiter = new RateLimit(50, 1000); this.rateLimitExceeded = new Set(); this.settings = { @@ -66,8 +68,7 @@ export default class ServerTossupRoom extends TossupRoom { this.getSetList().then(setList => { this.setList = setList; }); } - async message(userId, message) { - + async message (userId, message) { switch (message.type) { case 'ban': return this.banUser(message.ownerId, message.target_user, message.targ_name); case 'chat': return this.chat(userId, message); @@ -83,7 +84,7 @@ export default class ServerTossupRoom extends TossupRoom { } } - votekickInit(targetId, targetName, sendingId) { + votekickInit (targetId, targetName, sendingId) { if (!this.lastVotekickTime) { this.lastVotekickTime = {}; } @@ -106,8 +107,8 @@ export default class ServerTossupRoom extends TossupRoom { return; } - let threshold = Math.max((Object.keys(this.players).length) - 1, 0); - let votekick = new Votekick(targetName, targetId, [], threshold); + const threshold = Math.max((Object.keys(this.players).length) - 1, 0); + const votekick = new Votekick(targetName, targetId, [], threshold); votekick.vote(sendingId); if (votekick.check()) { this.emitMessage({ type: 'successful-vk', targetName: votekick.targName, targetId: votekick.targId }); @@ -119,7 +120,7 @@ export default class ServerTossupRoom extends TossupRoom { console.log(this.votekickList); } - votekickVote(targetUser, votingId) { + votekickVote (targetUser, votingId) { let exists = false; let thisVotekick; this.votekickList.forEach((votekick) => { @@ -127,17 +128,16 @@ export default class ServerTossupRoom extends TossupRoom { thisVotekick = votekick; exists = true; } - }) + }); if (!exists) { return; } thisVotekick.vote(votingId); if (thisVotekick.check()) { - this.emitMessage({ type: 'successful-vk', targetName: thisVotekick.targName, targetId: thisVotekick.targId }) + this.emitMessage({ type: 'successful-vk', targetName: thisVotekick.targName, targetId: thisVotekick.targId }); this.kickedUserList.push(targetUser); } console.log(this.votekickList); - } connection (socket, userId, username) { From b3d1196a1b51b7b1c82fa68d531abfc7dc7b80f2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 5 Nov 2024 09:43:16 -0600 Subject: [PATCH 08/16] cleaned up, fixed VK + ban showing up on ai bot --- client/multiplayer/room.jsx | 1 - client/multiplayer/room.min.js | 2 +- client/scripts/upsertPlayerItem.js | 5 ++--- server/multiplayer/ServerTossupRoom.js | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index ee0f1dc4e..e85eee095 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -743,7 +743,6 @@ function togglePublic ({ public: isPublic, username }) { document.getElementById('toggle-login-required').checked = false; } Object.keys(players).forEach((player) => { - console.log(player); upsertPlayerItem(players[player], USER_ID, ownerId, socket, globalPublic); }); } diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 076377028..c071abcf1 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -2,4 +2,4 @@ import questionStats from"../scripts/auth/question-stats.js";import api from"../ * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"enforcing-ban":return ackBannedFromRoom();case"enforcing-kick":return ackKickedFromRoom();case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"force-username":return forceUsername(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"initiated-vk":return vkInit(b);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"successful-vk":return vkHandle(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}function ackKickedFromRoom(){window.alert("You were kicked from this room by players, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username -if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{console.log(a),upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function setYearRange({minYear:a,maxYear:b,username:c}){c&&logEvent(c,`changed the year range to ${a}-${b}`),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file +if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function setYearRange({minYear:a,maxYear:b,username:c}){c&&logEvent(c,`changed the year range to ${a}-${b}`),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index cfee6a8be..031139ece 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -27,7 +27,6 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu const playerItem = document.createElement('a'); playerItem.className = `list-group-item ${userId === USER_ID ? 'user-score' : ''} clickable`; playerItem.id = `list-group-${userId}`; - console.log('Is it public? ' + isPublic); const displayUsername = (playerIsOwner && !isPublic) ? `👑 ${escapeHTML(username)}` : escapeHTML(username); playerItem.innerHTML = ` @@ -62,7 +61,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu document.getElementById('player-list-group').appendChild(playerItem); // ban button if the viewer is the owner and the player is not, also room has to be private - if (iAmOwner && userId !== ownerId && !isPublic) { + if (iAmOwner && userId !== ownerId && !isPublic && userId !== 'ai-bot') { const banButton = document.createElement('button'); banButton.className = 'btn btn-danger btn-sm mt-2'; banButton.title = 'Ban an user. They can no longer join the room.'; @@ -76,7 +75,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu } let conditionalVK; if (!isPublic) { - if (ownerId === userId) { + if (ownerId === userId || userId === 'ai-bot') { conditionalVK = false; } else { conditionalVK = true; diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 691a49b8b..4cac467f4 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -222,9 +222,7 @@ export default class ServerTossupRoom extends TossupRoom { } owner_id (id) { - console.log('Recieved a owner-id request'); this.emitMessage({ type: 'owner-check', id }); - console.log('Owner check sent'); } chat (userId, { message }) { From 08391dc2981f98086000f8b69376273e734125ca Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 5 Nov 2024 13:05:58 -0600 Subject: [PATCH 09/16] Mute feature added --- client/multiplayer/room.jsx | 18 +++++++++++++++++- client/multiplayer/room.min.js | 4 ++-- client/scripts/upsertPlayerItem.js | 25 +++++++++++++++++++++++++ server/multiplayer/ServerTossupRoom.js | 4 ++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index e85eee095..671616d6a 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -14,6 +14,7 @@ let startingDifficulties = []; let ownerId = ''; let maxPacketNumber = 24; let globalPublic = true; +let muteList = []; /** * userId to player object */ @@ -67,6 +68,7 @@ socket.onmessage = function (event) { case 'join': return join(data); case 'leave': return leave(data); case 'lost-buzzer-race': return lostBuzzerRace(data); + case 'mute-notice': return mutePlayer(data); case 'next': return next(data); case 'no-questions-found': return noQuestionsFound(data); case 'owner-check': return ownerCheck(data); @@ -147,7 +149,10 @@ function buzz ({ userId, username }) { } } -function chat ({ message, userId, username }, live = false) { +function chat({ message, userId, username }, live = false) { + if (muteList.includes(userId)) { + return; + } if (!live && message === '') { document.getElementById('live-chat-' + userId).parentElement.remove(); return; @@ -503,6 +508,17 @@ function lostBuzzerRace ({ username, userId }) { logEvent(username, 'lost the buzzer race'); if (userId === USER_ID) { document.getElementById('answer-input-group').classList.add('d-none'); } } +function mutePlayer({ targetId, muteStatus }) { + if (muteStatus == 'Mute') { + if (!muteList.includes(targetId)) { + muteList.push(targetId) + } + } else { + if (muteList.includes(targetId)) { + muteList = muteList.filter(Id => Id !== targetId); + } + } +} function next ({ oldTossup, tossup: nextTossup, type, username }) { switch (type) { diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index c071abcf1..2d0758fa7 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,5 @@ -import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24,globalPublic=!0;/** +import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24,globalPublic=!0,muteList=[];/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"enforcing-ban":return ackBannedFromRoom();case"enforcing-kick":return ackKickedFromRoom();case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"force-username":return forceUsername(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"initiated-vk":return vkInit(b);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"successful-vk":return vkHandle(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}function ackKickedFromRoom(){window.alert("You were kicked from this room by players, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"enforcing-ban":return ackBannedFromRoom();case"enforcing-kick":return ackKickedFromRoom();case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"force-username":return forceUsername(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"initiated-vk":return vkInit(b);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"mute-notice":return mutePlayer(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"successful-vk":return vkHandle(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}function ackKickedFromRoom(){window.alert("You were kicked from this room by players, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!muteList.includes(c)){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0b!==a))}function next({oldTossup:a,tossup:b,type:c,username:d}){switch(c){case"next":logEvent(d,"went to the next question");break;case"skip":logEvent(d,"skipped the question");break;case"start":logEvent(d,"started the game");break;default:throw new Error("Invalid type")}"next"===c||"skip"===c?createTossupCard(a):"start"===c&&(document.getElementById("next").classList.add("btn-primary"),document.getElementById("next").classList.remove("btn-success"),document.getElementById("next").textContent="Next"),tossup=b,document.getElementById("packet-number-info").textContent=tossup?.packet.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-",document.getElementById("set-name-info").textContent=tossup?.set.name??"",document.getElementById("answer").textContent="",document.getElementById("question").textContent="",document.getElementById("buzz").textContent="Buzz",document.getElementById("buzz").disabled=!1,document.getElementById("pause").textContent="Pause",document.getElementById("pause").disabled=!1,document.getElementById("settings").classList.add("d-none"),showSkipButton(),updateTimerDisplay(100)}function noQuestionsFound(){window.alert("No questions found")}let resolveOwnerId;function ownerCheck({id:a}){ownerId=a,resolveOwnerId&&(resolveOwnerId(),resolveOwnerId=null)}async function getRecvOwnerId(){return await new Promise(a=>{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function setYearRange({minYear:a,maxYear:b,username:c}){c&&logEvent(c,`changed the year range to ${a}-${b}`),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index 031139ece..9bf6c2357 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -73,6 +73,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); }); } + //whether to allow VK or not let conditionalVK; if (!isPublic) { if (ownerId === userId || userId === 'ai-bot') { @@ -84,6 +85,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu conditionalVK = true; } + //votekick button. cannot vk an owner (change? idk) if (userId !== USER_ID && conditionalVK) { const vkButton = document.createElement('button'); vkButton.className = 'btn btn-warning btn-sm mt-2'; @@ -105,6 +107,29 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu }, 90000); }); } + //User cannot be ai, yourself, and the room has to be private + if (userId !== 'ai-bot' && userId !== USER_ID && !isPublic) { + + + const muteButton = document.createElement('button'); + + muteButton.className = 'btn btn-warning btn-sm mt-2'; + muteButton.title = 'Mute/Unmute an user to change visibility of what they say in chat.'; + muteButton.innerText = 'Mute'; + + playerItem.appendChild(muteButton); + + muteButton.addEventListener('click', () => { + socket.send(JSON.stringify({ type: 'mute-toggle', targetId: userId, sendingMuteId: USER_ID, muteStatus: muteButton.innerText })); + if (muteButton.innerText === 'Unmute') { + muteButton.innerText = `Mute`; + } else { + muteButton.innerText = `Unmute`; + } + + + }); + } // bootstrap requires "new" to be called on each popover // eslint-disable-next-line no-new diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 4cac467f4..3b9625f14 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -76,6 +76,7 @@ export default class ServerTossupRoom extends TossupRoom { case 'give-answer-live-update': return this.giveAnswerLiveUpdate(userId, message); case 'toggle-lock': return this.toggleLock(userId, message); case 'toggle-login-required': return this.toggleLoginRequired(userId, message); + case 'mute-toggle': return this.toggleMute(message.targetId, message.sendingMuteId, message.muteStatus) case 'toggle-public': return this.togglePublic(userId, message); case 'owner-id': return this.owner_id(this.ownerId); case 'votekick-init': return this.votekickInit(message.target_user, message.targ_name, message.send_id); @@ -83,6 +84,9 @@ export default class ServerTossupRoom extends TossupRoom { default: super.message(userId, message); } } + toggleMute(targetId, senderId, muteStatus) { + this.sendToSocket(senderId, {type: 'mute-notice', targetId, muteStatus}) + } votekickInit (targetId, targetName, sendingId) { if (!this.lastVotekickTime) { From 76e84cda23221a12cd871819b2f8e6f27a652ddf Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 5 Nov 2024 13:06:54 -0600 Subject: [PATCH 10/16] linting --- client/multiplayer/room.jsx | 8 ++++---- client/scripts/upsertPlayerItem.js | 16 ++++++---------- server/multiplayer/ServerTossupRoom.js | 7 ++++--- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index 671616d6a..7d636d314 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -149,7 +149,7 @@ function buzz ({ userId, username }) { } } -function chat({ message, userId, username }, live = false) { +function chat ({ message, userId, username }, live = false) { if (muteList.includes(userId)) { return; } @@ -508,10 +508,10 @@ function lostBuzzerRace ({ username, userId }) { logEvent(username, 'lost the buzzer race'); if (userId === USER_ID) { document.getElementById('answer-input-group').classList.add('d-none'); } } -function mutePlayer({ targetId, muteStatus }) { - if (muteStatus == 'Mute') { +function mutePlayer ({ targetId, muteStatus }) { + if (muteStatus === 'Mute') { if (!muteList.includes(targetId)) { - muteList.push(targetId) + muteList.push(targetId); } } else { if (muteList.includes(targetId)) { diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index 9bf6c2357..d8198a1be 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -73,7 +73,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); }); } - //whether to allow VK or not + // whether to allow VK or not let conditionalVK; if (!isPublic) { if (ownerId === userId || userId === 'ai-bot') { @@ -85,7 +85,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu conditionalVK = true; } - //votekick button. cannot vk an owner (change? idk) + // votekick button. cannot vk an owner (change? idk) if (userId !== USER_ID && conditionalVK) { const vkButton = document.createElement('button'); vkButton.className = 'btn btn-warning btn-sm mt-2'; @@ -107,12 +107,10 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu }, 90000); }); } - //User cannot be ai, yourself, and the room has to be private + // User cannot be ai, yourself, and the room has to be private if (userId !== 'ai-bot' && userId !== USER_ID && !isPublic) { - - const muteButton = document.createElement('button'); - + muteButton.className = 'btn btn-warning btn-sm mt-2'; muteButton.title = 'Mute/Unmute an user to change visibility of what they say in chat.'; muteButton.innerText = 'Mute'; @@ -122,12 +120,10 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu muteButton.addEventListener('click', () => { socket.send(JSON.stringify({ type: 'mute-toggle', targetId: userId, sendingMuteId: USER_ID, muteStatus: muteButton.innerText })); if (muteButton.innerText === 'Unmute') { - muteButton.innerText = `Mute`; + muteButton.innerText = 'Mute'; } else { - muteButton.innerText = `Unmute`; + muteButton.innerText = 'Unmute'; } - - }); } diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 3b9625f14..7bda3964c 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -76,7 +76,7 @@ export default class ServerTossupRoom extends TossupRoom { case 'give-answer-live-update': return this.giveAnswerLiveUpdate(userId, message); case 'toggle-lock': return this.toggleLock(userId, message); case 'toggle-login-required': return this.toggleLoginRequired(userId, message); - case 'mute-toggle': return this.toggleMute(message.targetId, message.sendingMuteId, message.muteStatus) + case 'mute-toggle': return this.toggleMute(message.targetId, message.sendingMuteId, message.muteStatus); case 'toggle-public': return this.togglePublic(userId, message); case 'owner-id': return this.owner_id(this.ownerId); case 'votekick-init': return this.votekickInit(message.target_user, message.targ_name, message.send_id); @@ -84,8 +84,9 @@ export default class ServerTossupRoom extends TossupRoom { default: super.message(userId, message); } } - toggleMute(targetId, senderId, muteStatus) { - this.sendToSocket(senderId, {type: 'mute-notice', targetId, muteStatus}) + + toggleMute (targetId, senderId, muteStatus) { + this.sendToSocket(senderId, { type: 'mute-notice', targetId, muteStatus }); } votekickInit (targetId, targetName, sendingId) { From a33fc76102268d1cd1d0a3af27ce786b17d42286 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 5 Nov 2024 13:13:20 -0600 Subject: [PATCH 11/16] organizing --- client/multiplayer/room.jsx | 103 +++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index 7d636d314..ef093213d 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -12,6 +12,7 @@ const categoryManager = new CategoryManager(); let oldCategories = JSON.stringify(categoryManager.export()); let startingDifficulties = []; let ownerId = ''; +let resolveOwnerId; let maxPacketNumber = 24; let globalPublic = true; let muteList = []; @@ -99,19 +100,7 @@ socket.onmessage = function (event) { case 'verified-ban': return recvBan(data); } }; -function vkInit ({ targetName, targetId, threshold }) { - logEvent('A votekick has been started against user ' + targetName + ' and needs ' + threshold + ' votes to suceed.'); -} -function vkHandle ({ targetName, targetId }) { - if (USER_ID === targetId) { - window.alert('You were vote kicked from this room by others.'); - setTimeout(() => { - window.location.replace('../'); - }, 100); - } else { - logEvent(targetName + ' has been vote kicked from this room.'); - } -} + function ackKickedFromRoom () { window.alert('You were kicked from this room by players, and cannot rejoin it.'); setTimeout(() => { @@ -125,16 +114,6 @@ function ackBannedFromRoom () { window.location.replace('../'); }, 100); } -function recvBan ({ target, targetUsername }) { - if (target === USER_ID) { - window.alert('You were banned from this room by the room owner.'); - setTimeout(() => { - window.location.replace('../'); - }, 100); - } else { - logEvent(targetUsername + ' has been banned from this room.'); - } -} function buzz ({ userId, username }) { logEvent(username, 'buzzed'); @@ -265,15 +244,6 @@ function connectionAcknowledged ({ USER_ID = userId; window.localStorage.setItem('USER_ID', USER_ID); } -async function processPlayers (messagePlayers) { - const recvOwnerId = await getRecvOwnerId(); - await Promise.all(Object.keys(messagePlayers).map(async (userId) => { - messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; - players[userId] = messagePlayers[userId]; - - upsertPlayerItem(players[userId], USER_ID, recvOwnerId, socket, globalPublic); - })); -} async function connectionAcknowledgedQuery ({ difficulties = [], @@ -335,6 +305,14 @@ function forceUsername ({ message, username }) { document.querySelector('#username').value = username; } +async function getRecvOwnerId () { + await new Promise((resolve) => { + resolveOwnerId = resolve; + socket.send(JSON.stringify({ type: 'owner-id' })); + }); + return ownerId; +} + async function giveAnswer ({ celerity, directive, directedPrompt, givenAnswer, perQuestionCelerity, score, tossup, userId, username }) { document.getElementById('answer-input').value = ''; document.getElementById('answer-input-group').classList.add('d-none'); @@ -565,30 +543,40 @@ function noQuestionsFound () { window.alert('No questions found'); } -let resolveOwnerId; - function ownerCheck ({ id }) { ownerId = id; - // Resolve the Promise to indicate that ownerId is set if (resolveOwnerId) { resolveOwnerId(); - resolveOwnerId = null; // Clear resolveOwnerId to prevent unintended future calls + resolveOwnerId = null; } } -async function getRecvOwnerId () { - await new Promise((resolve) => { - resolveOwnerId = resolve; - socket.send(JSON.stringify({ type: 'owner-id' })); - }); - return ownerId; -} - function pause ({ paused, username }) { logEvent(username, `${paused ? '' : 'un'}paused the game`); } +async function processPlayers (messagePlayers) { + const recvOwnerId = await getRecvOwnerId(); + await Promise.all(Object.keys(messagePlayers).map(async (userId) => { + messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; + players[userId] = messagePlayers[userId]; + + upsertPlayerItem(players[userId], USER_ID, recvOwnerId, socket, globalPublic); + })); +} + +function recvBan ({ target, targetUsername }) { + if (target === USER_ID) { + window.alert('You were banned from this room by the room owner.'); + setTimeout(() => { + window.location.replace('../'); + }, 100); + } else { + logEvent(targetUsername + ' has been banned from this room.'); + } +} + function revealAnswer ({ answer, question }) { document.getElementById('question').innerHTML = question; document.getElementById('answer').innerHTML = 'ANSWER: ' + answer; @@ -692,6 +680,15 @@ function setUsername ({ oldUsername, newUsername, userId }) { upsertPlayerItem(players[userId], USER_ID, ownerId, socket, globalPublic); } +function setYearRange ({ minYear, maxYear, username }) { + if (username) { logEvent(username, `changed the year range to ${minYear}-${maxYear}`); } + + $('#slider').slider('values', 0, minYear); + $('#slider').slider('values', 1, maxYear); + document.getElementById('year-range-a').textContent = minYear; + document.getElementById('year-range-b').textContent = maxYear; +} + function toggleLock ({ lock, username }) { logEvent(username, `${lock ? 'locked' : 'unlocked'} the room`); document.getElementById('toggle-lock').checked = lock; @@ -776,13 +773,19 @@ function updateTimerDisplay (time) { document.querySelector('.timer .fraction').innerText = '.' + tenths; } -function setYearRange ({ minYear, maxYear, username }) { - if (username) { logEvent(username, `changed the year range to ${minYear}-${maxYear}`); } +function vkInit ({ targetName, targetId, threshold }) { + logEvent('A votekick has been started against user ' + targetName + ' and needs ' + threshold + ' votes to suceed.'); +} - $('#slider').slider('values', 0, minYear); - $('#slider').slider('values', 1, maxYear); - document.getElementById('year-range-a').textContent = minYear; - document.getElementById('year-range-b').textContent = maxYear; +function vkHandle ({ targetName, targetId }) { + if (USER_ID === targetId) { + window.alert('You were vote kicked from this room by others.'); + setTimeout(() => { + window.location.replace('../'); + }, 100); + } else { + logEvent(targetName + ' has been vote kicked from this room.'); + } } document.getElementById('answer-form').addEventListener('submit', function (event) { From 65bde5ecfa0b3e630f725761021f7a47a5a245c5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 6 Nov 2024 14:38:33 -0600 Subject: [PATCH 12/16] finalized - features room owner, banning, muting, vote kicking --- client/multiplayer/room.jsx | 20 +-- client/multiplayer/room.min.js | 7 +- client/scripts/upsertPlayerItem.js | 32 +--- server/multiplayer/ServerTossupRoom.js | 207 +++++++++++++------------ 4 files changed, 122 insertions(+), 144 deletions(-) diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index ef093213d..a38807627 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -58,8 +58,7 @@ socket.onmessage = function (event) { case 'connection-acknowledged': return connectionAcknowledged(data); case 'connection-acknowledged-query': return connectionAcknowledgedQuery(data); case 'connection-acknowledged-tossup': return connectionAcknowledgedTossup(data); - case 'enforcing-ban': return ackBannedFromRoom(); - case 'enforcing-kick': return ackKickedFromRoom(); + case 'enforcing-removal': return ackRemovedFromRoom(data); case 'end-of-set': return endOfSet(data); case 'error': return handleError(data); case 'force-username': return forceUsername(data); @@ -100,16 +99,13 @@ socket.onmessage = function (event) { case 'verified-ban': return recvBan(data); } }; - -function ackKickedFromRoom () { - window.alert('You were kicked from this room by players, and cannot rejoin it.'); - setTimeout(() => { - window.location.replace('../'); - }, 100); -} - -function ackBannedFromRoom () { - window.alert('You were banned from this room by the room owner, and cannot rejoin it.'); +// if a banned/kicked user tries to join a room they were removed from this is the response +function ackRemovedFromRoom ({ removalType }) { + if (removalType === 'kick') { + window.alert('You were kicked from this room by players, and cannot rejoin it.'); + } else { + window.alert('You were banned from this room by the room owner, and cannot rejoin it.'); + } setTimeout(() => { window.location.replace('../'); }, 100); diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 2d0758fa7..43825a55b 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,5 +1,6 @@ -import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24,globalPublic=!0,muteList=[];/** +import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let resolveOwnerId,oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24,globalPublic=!0,muteList=[];/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"enforcing-ban":return ackBannedFromRoom();case"enforcing-kick":return ackKickedFromRoom();case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"force-username":return forceUsername(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"initiated-vk":return vkInit(b);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"mute-notice":return mutePlayer(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"successful-vk":return vkHandle(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}function ackKickedFromRoom(){window.alert("You were kicked from this room by players, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function ackBannedFromRoom(){window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!muteList.includes(c)){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0b!==a))}function next({oldTossup:a,tossup:b,type:c,username:d}){switch(c){case"next":logEvent(d,"went to the next question");break;case"skip":logEvent(d,"skipped the question");break;case"start":logEvent(d,"started the game");break;default:throw new Error("Invalid type")}"next"===c||"skip"===c?createTossupCard(a):"start"===c&&(document.getElementById("next").classList.add("btn-primary"),document.getElementById("next").classList.remove("btn-success"),document.getElementById("next").textContent="Next"),tossup=b,document.getElementById("packet-number-info").textContent=tossup?.packet.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-",document.getElementById("set-name-info").textContent=tossup?.set.name??"",document.getElementById("answer").textContent="",document.getElementById("question").textContent="",document.getElementById("buzz").textContent="Buzz",document.getElementById("buzz").disabled=!1,document.getElementById("pause").textContent="Pause",document.getElementById("pause").disabled=!1,document.getElementById("settings").classList.add("d-none"),showSkipButton(),updateTimerDisplay(100)}function noQuestionsFound(){window.alert("No questions found")}let resolveOwnerId;function ownerCheck({id:a}){ownerId=a,resolveOwnerId&&(resolveOwnerId(),resolveOwnerId=null)}async function getRecvOwnerId(){return await new Promise(a=>{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username -if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function setYearRange({minYear:a,maxYear:b,username:c}){c&&logEvent(c,`changed the year range to ${a}-${b}`),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"enforcing-removal":return ackRemovedFromRoom(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"force-username":return forceUsername(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"initiated-vk":return vkInit(b);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"mute-notice":return mutePlayer(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"successful-vk":return vkHandle(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};// if a banned/kicked user tries to join a room they were removed from this is the response +function ackRemovedFromRoom({removalType:a}){"kick"===a?window.alert("You were kicked from this room by players, and cannot rejoin it."):window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!muteList.includes(c)){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function getRecvOwnerId(){return await new Promise(a=>{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0b!==a))}function next({oldTossup:a,tossup:b,type:c,username:d}){switch(c){case"next":logEvent(d,"went to the next question");break;case"skip":logEvent(d,"skipped the question");break;case"start":logEvent(d,"started the game");break;default:throw new Error("Invalid type")}"next"===c||"skip"===c?createTossupCard(a):"start"===c&&(document.getElementById("next").classList.add("btn-primary"),document.getElementById("next").classList.remove("btn-success"),document.getElementById("next").textContent="Next"),tossup=b,document.getElementById("packet-number-info").textContent=tossup?.packet.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-",document.getElementById("set-name-info").textContent=tossup?.set.name??"",document.getElementById("answer").textContent="",document.getElementById("question").textContent="",document.getElementById("buzz").textContent="Buzz",document.getElementById("buzz").disabled=!1,document.getElementById("pause").textContent="Pause",document.getElementById("pause").disabled=!1,document.getElementById("settings").classList.add("d-none"),showSkipButton(),updateTimerDisplay(100)}function noQuestionsFound(){window.alert("No questions found")}function ownerCheck({id:a}){ownerId=a,resolveOwnerId&&(resolveOwnerId(),resolveOwnerId=null)}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index d8198a1be..bf08779d3 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -6,6 +6,7 @@ import { escapeHTML } from './utilities/strings.js'; * @param {string} USER_ID - The item is highlighted blue if `USER_ID === player.userId`. * @param {string} ownerId - ID of the room owner */ +// overall handling of some of these mechanics in the upsertion section might not be best idea? works though export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPublic) { if (!player || !player.userId) { console.error('Player or player.userId is undefined', { player }); @@ -16,8 +17,6 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu const celerity = player?.celerity?.correct?.average ?? player?.celerity ?? 0; const playerIsOwner = ownerId === userId; - const iAmOwner = ownerId === USER_ID; - const res = playerIsOwner ? 'Yes' : 'No'; // Remove the existing player item if it exists if (document.getElementById('list-group-' + userId)) { @@ -54,69 +53,50 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu
  • Negs${negs}
  • TUH${tuh}
  • Celerity${celerity.toFixed(3)}
  • -
  • Is Owner?${res}
  • +
  • Is Owner?${playerIsOwner ? 'Yes' : 'No'}
  • `); document.getElementById('player-list-group').appendChild(playerItem); // ban button if the viewer is the owner and the player is not, also room has to be private - if (iAmOwner && userId !== ownerId && !isPublic && userId !== 'ai-bot') { + if ((ownerId === USER_ID) && userId !== ownerId && !isPublic && userId !== 'ai-bot') { const banButton = document.createElement('button'); banButton.className = 'btn btn-danger btn-sm mt-2'; banButton.title = 'Ban an user. They can no longer join the room.'; banButton.innerText = 'Ban'; - playerItem.appendChild(banButton); - banButton.addEventListener('click', () => { socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); }); } - // whether to allow VK or not - let conditionalVK; - if (!isPublic) { - if (ownerId === userId || userId === 'ai-bot') { - conditionalVK = false; - } else { - conditionalVK = true; - } - } else { - conditionalVK = true; - } // votekick button. cannot vk an owner (change? idk) - if (userId !== USER_ID && conditionalVK) { + if (userId !== USER_ID && (isPublic || (userId !== ownerId && userId !== 'ai-bot'))) { const vkButton = document.createElement('button'); vkButton.className = 'btn btn-warning btn-sm mt-2'; vkButton.title = 'Initiate a votekick on an user. 90 second cooldown.'; vkButton.innerText = 'VK'; - playerItem.appendChild(vkButton); - vkButton.addEventListener('click', () => { socket.send(JSON.stringify({ type: 'votekick-vote', target_user: userId, targ_name: username, send_id: USER_ID })); socket.send(JSON.stringify({ type: 'votekick-init', target_user: userId, targ_name: username, send_id: USER_ID })); - vkButton.disabled = true; vkButton.innerText = 'Cooldown'; - setTimeout(() => { vkButton.disabled = false; vkButton.innerText = 'VK'; }, 90000); }); } - // User cannot be ai, yourself, and the room has to be private - if (userId !== 'ai-bot' && userId !== USER_ID && !isPublic) { + // User cannot be ai or yourself + if (userId !== 'ai-bot' && userId !== USER_ID) { const muteButton = document.createElement('button'); muteButton.className = 'btn btn-warning btn-sm mt-2'; muteButton.title = 'Mute/Unmute an user to change visibility of what they say in chat.'; muteButton.innerText = 'Mute'; - playerItem.appendChild(muteButton); - muteButton.addEventListener('click', () => { socket.send(JSON.stringify({ type: 'mute-toggle', targetId: userId, sendingMuteId: USER_ID, muteStatus: muteButton.innerText })); if (muteButton.innerText === 'Unmute') { diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 7bda3964c..3c1f00ff5 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -12,36 +12,6 @@ import getNumPackets from '../../database/qbreader/get-num-packets.js'; import checkAnswer from 'qb-answer-checker'; -class Votekick { - constructor (targName, targId, voted = [], threshold) { - this.targName = targName; - this.targId = targId; - this.voted = Array.isArray(voted) ? voted : []; - this.threshold = threshold; - } - - exists (givenId) { - if (this.targId === givenId) { - return true; - } else { - return false; - } - } - - vote (votingId) { - if (!this.voted.includes(votingId)) { - this.voted.push(votingId); - } - } - - check () { - if (this.voted.length >= this.threshold) { - return true; - } else { - return false; - } - } -} export default class ServerTossupRoom extends TossupRoom { constructor (name, ownerId, isPermanent = false, categories = [], subcategories = [], alternateSubcategories = []) { super(name, categories, subcategories, alternateSubcategories); @@ -74,9 +44,9 @@ export default class ServerTossupRoom extends TossupRoom { case 'chat': return this.chat(userId, message); case 'chat-live-update': return this.chatLiveUpdate(userId, message); case 'give-answer-live-update': return this.giveAnswerLiveUpdate(userId, message); + case 'mute-toggle': return this.toggleMute(message.targetId, message.sendingMuteId, message.muteStatus); case 'toggle-lock': return this.toggleLock(userId, message); case 'toggle-login-required': return this.toggleLoginRequired(userId, message); - case 'mute-toggle': return this.toggleMute(message.targetId, message.sendingMuteId, message.muteStatus); case 'toggle-public': return this.togglePublic(userId, message); case 'owner-id': return this.owner_id(this.ownerId); case 'votekick-init': return this.votekickInit(message.target_user, message.targ_name, message.send_id); @@ -85,64 +55,13 @@ export default class ServerTossupRoom extends TossupRoom { } } - toggleMute (targetId, senderId, muteStatus) { - this.sendToSocket(senderId, { type: 'mute-notice', targetId, muteStatus }); - } - - votekickInit (targetId, targetName, sendingId) { - if (!this.lastVotekickTime) { - this.lastVotekickTime = {}; - } - - const currentTime = Date.now(); - if (this.lastVotekickTime[sendingId] && (currentTime - this.lastVotekickTime[sendingId] < 90000)) { - console.log(`Votekick denied: ${sendingId} 90 second cooldown viol.`); - return; - } - - this.lastVotekickTime[sendingId] = currentTime; - - let exists = false; - this.votekickList.forEach((votekick) => { - if (votekick.exists(targetId)) { - exists = true; - } - }); - if (exists) { - return; - } - - const threshold = Math.max((Object.keys(this.players).length) - 1, 0); - const votekick = new Votekick(targetName, targetId, [], threshold); - votekick.vote(sendingId); - if (votekick.check()) { - this.emitMessage({ type: 'successful-vk', targetName: votekick.targName, targetId: votekick.targId }); - this.kickedUserList.push(targetId); - } else { - this.votekickList.push(votekick); - this.emitMessage({ type: 'initiated-vk', targetName: votekick.targName, targetId: votekick.targId, threshold }); - } - console.log(this.votekickList); - } - - votekickVote (targetUser, votingId) { - let exists = false; - let thisVotekick; - this.votekickList.forEach((votekick) => { - if (votekick.exists(targetUser)) { - thisVotekick = votekick; - exists = true; - } - }); - if (!exists) { - return; - } - thisVotekick.vote(votingId); - if (thisVotekick.check()) { - this.emitMessage({ type: 'successful-vk', targetName: thisVotekick.targName, targetId: thisVotekick.targId }); - this.kickedUserList.push(targetUser); + banUser (ownerId, targetUser, targetUsername) { + console.log('Ban request recieved. Target ' + targetUser); + if (this.ownerId === ownerId) { + console.log('Checked, owner sent ban'); + this.emitMessage({ type: 'verified-ban', target: targetUser, targetUsername }); + this.bannedUserList.push(targetUser); } - console.log(this.votekickList); } connection (socket, userId, username) { @@ -155,12 +74,12 @@ export default class ServerTossupRoom extends TossupRoom { username = this.players[userId].safelySetUsername(username); if (this.bannedUserList.includes(userId)) { console.log('Banned user ' + userId + ' (' + username + ') tried to join a room'); - this.sendToSocket(userId, { type: 'enforcing-ban' }); + this.sendToSocket(userId, { type: 'enforcing-removal', removalType: 'ban' }); return; } if (this.kickedUserList.includes(userId)) { console.log('Kicked user ' + userId + ' (' + username + ') tried to join a room'); - this.sendToSocket(userId, { type: 'enforcing-kick' }); + this.sendToSocket(userId, { type: 'enforcing-removal', removalType: 'kick' }); return; } @@ -217,19 +136,6 @@ export default class ServerTossupRoom extends TossupRoom { this.emitMessage({ type: 'join', isNew, userId, username, user: this.players[userId] }); } - banUser (ownerId, targetUser, targetUsername) { - console.log('Ban request recieved. Target ' + targetUser); - if (this.ownerId === ownerId) { - console.log('Checked, owner sent ban'); - this.emitMessage({ type: 'verified-ban', target: targetUser, targetUsername }); - this.bannedUserList.push(targetUser); - } - } - - owner_id (id) { - this.emitMessage({ type: 'owner-check', id }); - } - chat (userId, { message }) { // prevent chat messages if room is public, since they can still be sent with API if (this.settings.public || typeof message !== 'string') { return false; } @@ -264,6 +170,10 @@ export default class ServerTossupRoom extends TossupRoom { super.next(userId, { type }); } + owner_id (id) { + this.emitMessage({ type: 'owner-check', id }); + } + setCategories (userId, { categories, subcategories, alternateSubcategories, percentView, categoryPercents }) { if (this.isPermanent) { return; } super.setCategories(userId, { categories, subcategories, alternateSubcategories, percentView, categoryPercents }); @@ -313,6 +223,10 @@ export default class ServerTossupRoom extends TossupRoom { this.emitMessage({ type: 'toggle-login-required', loginRequired, username }); } + toggleMute (targetId, senderId, muteStatus) { + this.sendToSocket(senderId, { type: 'mute-notice', targetId, muteStatus }); + } + togglePublic (userId, { public: isPublic }) { if (this.isPermanent) { return; } this.settings.public = isPublic; @@ -337,4 +251,91 @@ export default class ServerTossupRoom extends TossupRoom { if (this.settings.public) { return; } super.toggleTimer(userId, { timer }); } + + votekickInit (targetId, targetName, sendingId) { + if (!this.lastVotekickTime) { + this.lastVotekickTime = {}; + } + + const currentTime = Date.now(); + if (this.lastVotekickTime[sendingId] && (currentTime - this.lastVotekickTime[sendingId] < 90000)) { + console.log(`Votekick denied: ${sendingId} 90 second cooldown viol.`); + return; + } + + this.lastVotekickTime[sendingId] = currentTime; + + let exists = false; + this.votekickList.forEach((votekick) => { + if (votekick.exists(targetId)) { + exists = true; + } + }); + if (exists) { + return; + } + + const threshold = Math.max((Object.keys(this.players).length) - 1, 0); + const votekick = new Votekick(targetName, targetId, [], threshold); + votekick.vote(sendingId); + if (votekick.check()) { + this.emitMessage({ type: 'successful-vk', targetName: votekick.targName, targetId: votekick.targId }); + this.kickedUserList.push(targetId); + } else { + this.votekickList.push(votekick); + this.emitMessage({ type: 'initiated-vk', targetName: votekick.targName, targetId: votekick.targId, threshold }); + } + console.log(this.votekickList); + } + + votekickVote (targetUser, votingId) { + let exists = false; + let thisVotekick; + this.votekickList.forEach((votekick) => { + if (votekick.exists(targetUser)) { + thisVotekick = votekick; + exists = true; + } + }); + if (!exists) { + return; + } + thisVotekick.vote(votingId); + if (thisVotekick.check()) { + this.emitMessage({ type: 'successful-vk', targetName: thisVotekick.targName, targetId: thisVotekick.targId }); + this.kickedUserList.push(targetUser); + } + console.log(this.votekickList); + } +} + +class Votekick { + constructor (targName, targId, voted = [], threshold) { + this.targName = targName; + this.targId = targId; + this.voted = Array.isArray(voted) ? voted : []; + this.threshold = threshold; + } + + exists (givenId) { + if (this.targId === givenId) { + return true; + } else { + return false; + } + } + + vote (votingId) { + if (!this.voted.includes(votingId)) { + this.voted.push(votingId); + } + } + + check () { + if (this.voted.length >= this.threshold) { + return true; + } else { + return false; + } + } } From e13e9d1498a1d806eaee2d536132ae61665ecb31 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 7 Nov 2024 19:12:25 -0600 Subject: [PATCH 13/16] silly bug fix --- client/scripts/upsertPlayerItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index bf08779d3..20983ae2f 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -90,7 +90,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu }); } // User cannot be ai or yourself - if (userId !== 'ai-bot' && userId !== USER_ID) { + if (userId !== 'ai-bot' && userId !== USER_ID && !isPublic) { const muteButton = document.createElement('button'); muteButton.className = 'btn btn-warning btn-sm mt-2'; From 08ac462ab7aeea888430ff9eb75f6fa393ba54c7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 8 Nov 2024 20:57:15 -0600 Subject: [PATCH 14/16] Added documentation of the VK, mute, and ban functions in the about page --- client/about/index.html | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/client/about/index.html b/client/about/index.html index 94067624f..f83bef2f8 100644 --- a/client/about/index.html +++ b/client/about/index.html @@ -130,6 +130,20 @@

    Does it cost money to create an account?

    Geoword is the only feature that costs money. All of the other features of QB Reader are completely free.

    +

    What do I do if someone is causing trouble in a room?

    +

    + If a player in a public room is causing issues, such as spamming, advertising, or being expletive, you have a few options. +

    +
      +
    • You can initiate a votekick on a player by using the "VK" under their name. + If enough players also press the votekick, then the user will be banned from that room. + They will not be able to rejoin.
    • +
    • When the room is private and you are the owner, you can outright ban the player instead. + Press the "Ban" button under an user's name and they will be banned from the room and cannot rejoin.
    • +
    • If you wish to not see the messages of a certain player, you can mute them. + Use the "Mute" button under the player's name and you will no longer see chat messages from them. + You can unmute an user at any time to see their messages again.
    • +

    Where can I request a set?

    You can do so @@ -156,4 +170,4 @@

    Packet List:

    - + \ No newline at end of file From c41f5559de4c570849f119d6a8c67e5a7d89ea29 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Mon, 11 Nov 2024 22:40:01 -0500 Subject: [PATCH 15/16] add spacing between vk buttons --- client/scripts/upsertPlayerItem.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index 20983ae2f..e4566b8fa 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -62,7 +62,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu // ban button if the viewer is the owner and the player is not, also room has to be private if ((ownerId === USER_ID) && userId !== ownerId && !isPublic && userId !== 'ai-bot') { const banButton = document.createElement('button'); - banButton.className = 'btn btn-danger btn-sm mt-2'; + banButton.className = 'btn btn-danger btn-sm mt-2 me-1'; banButton.title = 'Ban an user. They can no longer join the room.'; banButton.innerText = 'Ban'; playerItem.appendChild(banButton); @@ -74,7 +74,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu // votekick button. cannot vk an owner (change? idk) if (userId !== USER_ID && (isPublic || (userId !== ownerId && userId !== 'ai-bot'))) { const vkButton = document.createElement('button'); - vkButton.className = 'btn btn-warning btn-sm mt-2'; + vkButton.className = 'btn btn-warning btn-sm mt-2 me-1'; vkButton.title = 'Initiate a votekick on an user. 90 second cooldown.'; vkButton.innerText = 'VK'; playerItem.appendChild(vkButton); @@ -93,7 +93,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu if (userId !== 'ai-bot' && userId !== USER_ID && !isPublic) { const muteButton = document.createElement('button'); - muteButton.className = 'btn btn-warning btn-sm mt-2'; + muteButton.className = 'btn btn-warning btn-sm mt-2 me-1'; muteButton.title = 'Mute/Unmute an user to change visibility of what they say in chat.'; muteButton.innerText = 'Mute'; playerItem.appendChild(muteButton); From af19104e66b9f7d4b015cd1498142da0e0bf73e3 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Mon, 11 Nov 2024 23:55:32 -0500 Subject: [PATCH 16/16] simplify code and fix security of checking ownerId --- client/multiplayer/room.jsx | 72 +++++---------- client/multiplayer/room.min.js | 8 +- client/scripts/upsertPlayerItem.js | 26 +++--- quizbowl/Player.js | 5 - server/multiplayer/ServerTossupRoom.js | 122 +++++++++---------------- server/multiplayer/VoteKick.js | 21 +++++ 6 files changed, 103 insertions(+), 151 deletions(-) create mode 100644 server/multiplayer/VoteKick.js diff --git a/client/multiplayer/room.jsx b/client/multiplayer/room.jsx index a38807627..6e465801e 100644 --- a/client/multiplayer/room.jsx +++ b/client/multiplayer/room.jsx @@ -12,7 +12,6 @@ const categoryManager = new CategoryManager(); let oldCategories = JSON.stringify(categoryManager.export()); let startingDifficulties = []; let ownerId = ''; -let resolveOwnerId; let maxPacketNumber = 24; let globalPublic = true; let muteList = []; @@ -55,6 +54,7 @@ socket.onmessage = function (event) { case 'chat': return chat(data, false); case 'chat-live-update': return chat(data, true); case 'clear-stats': return clearStats(data); + case 'confirm-ban': return confirmBan(data); case 'connection-acknowledged': return connectionAcknowledged(data); case 'connection-acknowledged-query': return connectionAcknowledgedQuery(data); case 'connection-acknowledged-tossup': return connectionAcknowledgedTossup(data); @@ -68,10 +68,9 @@ socket.onmessage = function (event) { case 'join': return join(data); case 'leave': return leave(data); case 'lost-buzzer-race': return lostBuzzerRace(data); - case 'mute-notice': return mutePlayer(data); + case 'mute-player': return mutePlayer(data); case 'next': return next(data); case 'no-questions-found': return noQuestionsFound(data); - case 'owner-check': return ownerCheck(data); case 'pause': return pause(data); case 'reveal-answer': return revealAnswer(data); case 'set-categories': return setCategories(data); @@ -96,7 +95,6 @@ socket.onmessage = function (event) { case 'toggle-standard-only': return toggleStandardOnly(data); case 'toggle-timer': return toggleTimer(data); case 'update-question': return updateQuestion(data); - case 'verified-ban': return recvBan(data); } }; // if a banned/kicked user tries to join a room they were removed from this is the response @@ -167,10 +165,22 @@ function clearStats ({ userId }) { sortPlayerListGroup(); } +function confirmBan ({ targetId, targetUsername }) { + if (targetId === USER_ID) { + window.alert('You were banned from this room by the room owner.'); + setTimeout(() => { + window.location.replace('../'); + }, 100); + } else { + logEvent(targetUsername + ' has been banned from this room.'); + } +} + function connectionAcknowledged ({ buzzedIn, canBuzz, isPermanent, + ownerId: serverOwnerId, players: messagePlayers, questionProgress, settings, @@ -186,8 +196,12 @@ function connectionAcknowledged ({ document.getElementById('private-chat-warning').innerHTML = 'This is a permanent room. Some settings have been restricted.'; } - processPlayers(messagePlayers); - + ownerId = serverOwnerId; + for (const userId of Object.keys(messagePlayers)) { + messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; + players[userId] = messagePlayers[userId]; + upsertPlayerItem(players[userId], USER_ID, ownerId, socket, globalPublic); + } sortPlayerListGroup(); switch (questionProgress) { @@ -301,14 +315,6 @@ function forceUsername ({ message, username }) { document.querySelector('#username').value = username; } -async function getRecvOwnerId () { - await new Promise((resolve) => { - resolveOwnerId = resolve; - socket.send(JSON.stringify({ type: 'owner-id' })); - }); - return ownerId; -} - async function giveAnswer ({ celerity, directive, directedPrompt, givenAnswer, perQuestionCelerity, score, tossup, userId, username }) { document.getElementById('answer-input').value = ''; document.getElementById('answer-input-group').classList.add('d-none'); @@ -539,40 +545,10 @@ function noQuestionsFound () { window.alert('No questions found'); } -function ownerCheck ({ id }) { - ownerId = id; - - if (resolveOwnerId) { - resolveOwnerId(); - resolveOwnerId = null; - } -} - function pause ({ paused, username }) { logEvent(username, `${paused ? '' : 'un'}paused the game`); } -async function processPlayers (messagePlayers) { - const recvOwnerId = await getRecvOwnerId(); - await Promise.all(Object.keys(messagePlayers).map(async (userId) => { - messagePlayers[userId].celerity = messagePlayers[userId].celerity.correct.average; - players[userId] = messagePlayers[userId]; - - upsertPlayerItem(players[userId], USER_ID, recvOwnerId, socket, globalPublic); - })); -} - -function recvBan ({ target, targetUsername }) { - if (target === USER_ID) { - window.alert('You were banned from this room by the room owner.'); - setTimeout(() => { - window.location.replace('../'); - }, 100); - } else { - logEvent(targetUsername + ' has been banned from this room.'); - } -} - function revealAnswer ({ answer, question }) { document.getElementById('question').innerHTML = question; document.getElementById('answer').innerHTML = 'ANSWER: ' + answer; @@ -769,18 +745,18 @@ function updateTimerDisplay (time) { document.querySelector('.timer .fraction').innerText = '.' + tenths; } -function vkInit ({ targetName, targetId, threshold }) { - logEvent('A votekick has been started against user ' + targetName + ' and needs ' + threshold + ' votes to suceed.'); +function vkInit ({ targetUsername, threshold }) { + logEvent(`A votekick has been started against user ${targetUsername} and needs ${threshold} votes to suceed.`); } -function vkHandle ({ targetName, targetId }) { +function vkHandle ({ targetUsername, targetId }) { if (USER_ID === targetId) { window.alert('You were vote kicked from this room by others.'); setTimeout(() => { window.location.replace('../'); }, 100); } else { - logEvent(targetName + ' has been vote kicked from this room.'); + logEvent(targetUsername + ' has been vote kicked from this room.'); } } diff --git a/client/multiplayer/room.min.js b/client/multiplayer/room.min.js index 43825a55b..ab3298430 100644 --- a/client/multiplayer/room.min.js +++ b/client/multiplayer/room.min.js @@ -1,6 +1,6 @@ -import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let resolveOwnerId,oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24,globalPublic=!0,muteList=[];/** +import questionStats from"../scripts/auth/question-stats.js";import api from"../scripts/api/index.js";import audio from"../audio/index.js";import CategoryManager from"../../quizbowl/category-manager.js";import{getDropdownValues}from"../scripts/utilities/dropdown-checklist.js";import{arrayToRange,createTossupCard,rangeToArray}from"../scripts/utilities/index.js";import CategoryModal from"../scripts/components/CategoryModal.min.js";import DifficultyDropdown from"../scripts/components/DifficultyDropdown.min.js";import upsertPlayerItem from"../scripts/upsertPlayerItem.js";const categoryManager=new CategoryManager;let oldCategories=JSON.stringify(categoryManager.export()),startingDifficulties=[],ownerId="",maxPacketNumber=24,globalPublic=!0,muteList=[];/** * userId to player object */const players={},ROOM_NAME=decodeURIComponent(window.location.pathname.substring(13));let tossup={},USER_ID=window.localStorage.getItem("USER_ID")||"unknown",username=window.localStorage.getItem("multiplayer-username")||api.getRandomName();const socket=new window.WebSocket(window.location.href.replace("http","ws")+(window.location.href.endsWith("?private=true")?"&":"?")+new URLSearchParams({roomName:ROOM_NAME,userId:USER_ID,username}).toString()),PING_INTERVAL_ID=setInterval(()=>socket.send(JSON.stringify({type:"ping"})),45e3);// Ping server every 45 seconds to prevent socket disconnection -socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"enforcing-removal":return ackRemovedFromRoom(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"force-username":return forceUsername(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"initiated-vk":return vkInit(b);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"mute-notice":return mutePlayer(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"owner-check":return ownerCheck(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"successful-vk":return vkHandle(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b);case"verified-ban":return recvBan(b)}};// if a banned/kicked user tries to join a room they were removed from this is the response -function ackRemovedFromRoom({removalType:a}){"kick"===a?window.alert("You were kicked from this room by players, and cannot rejoin it."):window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!muteList.includes(c)){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,players:d,questionProgress:e,settings:f,userId:g}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),processPlayers(d),sortPlayerListGroup();0===e?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===e?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===e?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=f.lock,document.getElementById("toggle-login-required").checked=f.loginRequired,document.getElementById("chat").disabled=f.public,document.getElementById("toggle-lock").disabled=f.public,document.getElementById("toggle-login-required").disabled=f.public,document.getElementById("toggle-timer").disabled=f.public,document.getElementById("toggle-public").checked=f.public,globalPublic=f.public,document.getElementById("reading-speed").value=f.readingSpeed,document.getElementById("reading-speed-display").textContent=f.readingSpeed,document.getElementById("strictness").value=f.strictness,document.getElementById("strictness-display").textContent=f.strictness,document.getElementById("toggle-rebuzz").checked=f.rebuzz,document.getElementById("toggle-skip").checked=f.skip,document.getElementById("timer").classList.toggle("d-none",!f.timer),document.getElementById("toggle-timer").checked=f.timer,USER_ID=g,window.localStorage.setItem("USER_ID",USER_ID)}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function getRecvOwnerId(){return await new Promise(a=>{resolveOwnerId=a,socket.send(JSON.stringify({type:"owner-id"}))}),ownerId}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0b!==a))}function next({oldTossup:a,tossup:b,type:c,username:d}){switch(c){case"next":logEvent(d,"went to the next question");break;case"skip":logEvent(d,"skipped the question");break;case"start":logEvent(d,"started the game");break;default:throw new Error("Invalid type")}"next"===c||"skip"===c?createTossupCard(a):"start"===c&&(document.getElementById("next").classList.add("btn-primary"),document.getElementById("next").classList.remove("btn-success"),document.getElementById("next").textContent="Next"),tossup=b,document.getElementById("packet-number-info").textContent=tossup?.packet.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-",document.getElementById("set-name-info").textContent=tossup?.set.name??"",document.getElementById("answer").textContent="",document.getElementById("question").textContent="",document.getElementById("buzz").textContent="Buzz",document.getElementById("buzz").disabled=!1,document.getElementById("pause").textContent="Pause",document.getElementById("pause").disabled=!1,document.getElementById("settings").classList.add("d-none"),showSkipButton(),updateTimerDisplay(100)}function noQuestionsFound(){window.alert("No questions found")}function ownerCheck({id:a}){ownerId=a,resolveOwnerId&&(resolveOwnerId(),resolveOwnerId=null)}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}async function processPlayers(a){const b=await getRecvOwnerId();await Promise.all(Object.keys(a).map(async c=>{a[c].celerity=a[c].celerity.correct.average,players[c]=a[c],upsertPlayerItem(players[c],USER_ID,b,socket,globalPublic)}))}function recvBan({target:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username -if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function vkInit({targetName:a,targetId:b,threshold:c}){logEvent("A votekick has been started against user "+a+" and needs "+c+" votes to suceed.")}function vkHandle({targetName:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file +socket.onclose=function(a){const{code:b}=a;3e3!==b&&window.alert("Disconnected from server"),clearInterval(PING_INTERVAL_ID)},socket.onmessage=function(a){const b=JSON.parse(a.data);switch(b.type){case"buzz":return buzz(b);case"chat":return chat(b,!1);case"chat-live-update":return chat(b,!0);case"clear-stats":return clearStats(b);case"confirm-ban":return confirmBan(b);case"connection-acknowledged":return connectionAcknowledged(b);case"connection-acknowledged-query":return connectionAcknowledgedQuery(b);case"connection-acknowledged-tossup":return connectionAcknowledgedTossup(b);case"enforcing-removal":return ackRemovedFromRoom(b);case"end-of-set":return endOfSet(b);case"error":return handleError(b);case"force-username":return forceUsername(b);case"give-answer":return giveAnswer(b);case"give-answer-live-update":return logGiveAnswer(b,!0);case"initiated-vk":return vkInit(b);case"join":return join(b);case"leave":return leave(b);case"lost-buzzer-race":return lostBuzzerRace(b);case"mute-player":return mutePlayer(b);case"next":return next(b);case"no-questions-found":return noQuestionsFound(b);case"pause":return pause(b);case"reveal-answer":return revealAnswer(b);case"set-categories":return setCategories(b);case"set-difficulties":return setDifficulties(b);case"set-reading-speed":return setReadingSpeed(b);case"set-packet-numbers":return setPacketNumbers(b);case"set-strictness":return setStrictness(b);case"set-set-name":return setSetName(b);case"set-username":return setUsername(b);case"set-year-range":return setYearRange(b);case"skip":return next(b);case"start":return next(b);case"successful-vk":return vkHandle(b);case"timer-update":return updateTimerDisplay(b.timeRemaining);case"toggle-lock":return toggleLock(b);case"toggle-login-required":return toggleLoginRequired(b);case"toggle-powermark-only":return togglePowermarkOnly(b);case"toggle-public":return togglePublic(b);case"toggle-rebuzz":return toggleRebuzz(b);case"toggle-select-by-set-name":return toggleSelectBySetName(b);case"toggle-skip":return toggleSkip(b);case"toggle-standard-only":return toggleStandardOnly(b);case"toggle-timer":return toggleTimer(b);case"update-question":return updateQuestion(b)}};// if a banned/kicked user tries to join a room they were removed from this is the response +function ackRemovedFromRoom({removalType:a}){"kick"===a?window.alert("You were kicked from this room by players, and cannot rejoin it."):window.alert("You were banned from this room by the room owner, and cannot rejoin it."),setTimeout(()=>{window.location.replace("../")},100)}function buzz({userId:a,username:b}){logEvent(b,"buzzed"),document.getElementById("buzz").disabled=!0,document.getElementById("pause").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("skip").disabled=!0,a===USER_ID&&(document.getElementById("answer-input-group").classList.remove("d-none"),document.getElementById("answer-input").focus())}function chat({message:a,userId:c,username:d},e=!1){if(!muteList.includes(c)){if(!e&&""===a)return void document.getElementById("live-chat-"+c).parentElement.remove();if(!e&&a)return document.getElementById("live-chat-"+c).className="",void(document.getElementById("live-chat-"+c).id="");if(document.getElementById("live-chat-"+c))return void(document.getElementById("live-chat-"+c).textContent=a);const f=document.createElement("b");f.textContent=d;const b=document.createElement("span");b.classList.add("text-muted"),b.id="live-chat-"+c,b.textContent=a;const g=document.createElement("li");g.appendChild(f),g.appendChild(document.createTextNode(" ")),g.appendChild(b),document.getElementById("room-history").prepend(g)}}function clearStats({userId:a}){for(const b of["celerity","negs","points","powers","tens","tuh","zeroes"])players[a][b]=0;upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()}function confirmBan({targetId:a,targetUsername:b}){a===USER_ID?(window.alert("You were banned from this room by the room owner."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(b+" has been banned from this room.")}function connectionAcknowledged({buzzedIn:a,canBuzz:b,isPermanent:c,ownerId:d,players:e,questionProgress:f,settings:g,userId:h}){document.getElementById("buzz").disabled=!b,c&&(document.getElementById("category-select-button").disabled=!0,document.getElementById("strictness").disabled=!0,document.getElementById("toggle-public").disabled=!0,document.getElementById("toggle-select-by-set-name").disabled=!0,document.getElementById("private-chat-warning").innerHTML="This is a permanent room. Some settings have been restricted."),ownerId=d;for(const i of Object.keys(e))e[i].celerity=e[i].celerity.correct.average,players[i]=e[i],upsertPlayerItem(players[i],USER_ID,ownerId,socket,globalPublic);sortPlayerListGroup();0===f?(document.getElementById("next").textContent="Start",document.getElementById("next").classList.remove("btn-primary"),document.getElementById("next").classList.add("btn-success")):1===f?(showSkipButton(),document.getElementById("settings").classList.add("d-none"),a?(document.getElementById("buzz").disabled=!0,document.getElementById("next").disabled=!0,document.getElementById("pause").disabled=!0):(document.getElementById("buzz").disabled=!1,document.getElementById("pause").disabled=!1)):2===f?(showNextButton(),document.getElementById("settings").classList.add("d-none")):void 0;document.getElementById("toggle-lock").checked=g.lock,document.getElementById("toggle-login-required").checked=g.loginRequired,document.getElementById("chat").disabled=g.public,document.getElementById("toggle-lock").disabled=g.public,document.getElementById("toggle-login-required").disabled=g.public,document.getElementById("toggle-timer").disabled=g.public,document.getElementById("toggle-public").checked=g.public,globalPublic=g.public,document.getElementById("reading-speed").value=g.readingSpeed,document.getElementById("reading-speed-display").textContent=g.readingSpeed,document.getElementById("strictness").value=g.strictness,document.getElementById("strictness-display").textContent=g.strictness,document.getElementById("toggle-rebuzz").checked=g.rebuzz,document.getElementById("toggle-skip").checked=g.skip,document.getElementById("timer").classList.toggle("d-none",!g.timer),document.getElementById("toggle-timer").checked=g.timer,USER_ID=h,window.localStorage.setItem("USER_ID",USER_ID)}async function connectionAcknowledgedQuery({difficulties:k=[],minYear:a,maxYear:b,packetNumbers:l=[],powermarkOnly:c,selectBySetName:d,setName:m="",standardOnly:e,alternateSubcategories:f,categories:g,subcategories:h,percentView:i,categoryPercents:j}){setDifficulties({difficulties:k}),$("#slider").slider("values",0,a),$("#slider").slider("values",1,b),document.getElementById("year-range-a").textContent=a,document.getElementById("year-range-b").textContent=b,document.getElementById("packet-number").value=arrayToRange(l),document.getElementById("toggle-powermark-only").checked=c,document.getElementById("difficulty-settings").classList.toggle("d-none",d),document.getElementById("set-settings").classList.toggle("d-none",!d),document.getElementById("toggle-select-by-set-name").checked=d,document.getElementById("toggle-powermark-only").disabled=d,document.getElementById("toggle-standard-only").disabled=d,document.getElementById("set-name").value=m,maxPacketNumber=await api.getNumPackets(m),""!==m&&0===maxPacketNumber&&document.getElementById("set-name").classList.add("is-invalid"),document.getElementById("toggle-standard-only").checked=e,categoryManager.import({categories:g,subcategories:h,alternateSubcategories:f,percentView:i,categoryPercents:j}),categoryManager.loadCategoryModal()}function connectionAcknowledgedTossup({tossup:a}){tossup=a,document.getElementById("set-name-info").textContent=tossup?.set?.name??"",document.getElementById("packet-number-info").textContent=tossup?.packet?.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-"}function endOfSet(){window.alert("You have reached the end of the set")}function forceUsername({message:a,username:b}){window.alert(a),window.localStorage.setItem("multiplayer-username",b),document.querySelector("#username").value=b}async function giveAnswer({celerity:a,directive:b,directedPrompt:c,givenAnswer:d,perQuestionCelerity:e,score:f,tossup:g,userId:h,username:i}){document.getElementById("answer-input").value="",document.getElementById("answer-input-group").classList.add("d-none"),document.getElementById("answer-input").blur(),logGiveAnswer({directive:b,message:d,username:i}),"prompt"===b&&c?logEvent(i,`was prompted with "${c}"`):"prompt"===b?logEvent(i,"was prompted"):logEvent(i,`${0{a.textContent=parseInt(a.innerHTML)+1})),"reject"===b&&(document.getElementById("buzz").disabled=!document.getElementById("toggle-rebuzz").checked&&h===USER_ID),10f&&players[h].negs++,players[h].points+=f,players[h].tuh++,players[h].celerity=a,upsertPlayerItem(players[h],USER_ID,ownerId,socket,globalPublic),sortPlayerListGroup()),"prompt"!==b&&h===USER_ID&&questionStats.recordTossup(g,0b!==a))}function next({oldTossup:a,tossup:b,type:c,username:d}){switch(c){case"next":logEvent(d,"went to the next question");break;case"skip":logEvent(d,"skipped the question");break;case"start":logEvent(d,"started the game");break;default:throw new Error("Invalid type")}"next"===c||"skip"===c?createTossupCard(a):"start"===c&&(document.getElementById("next").classList.add("btn-primary"),document.getElementById("next").classList.remove("btn-success"),document.getElementById("next").textContent="Next"),tossup=b,document.getElementById("packet-number-info").textContent=tossup?.packet.number??"-",document.getElementById("question-number-info").textContent=tossup?.number??"-",document.getElementById("set-name-info").textContent=tossup?.set.name??"",document.getElementById("answer").textContent="",document.getElementById("question").textContent="",document.getElementById("buzz").textContent="Buzz",document.getElementById("buzz").disabled=!1,document.getElementById("pause").textContent="Pause",document.getElementById("pause").disabled=!1,document.getElementById("settings").classList.add("d-none"),showSkipButton(),updateTimerDisplay(100)}function noQuestionsFound(){window.alert("No questions found")}function pause({paused:a,username:b}){logEvent(b,`${a?"":"un"}paused the game`)}function revealAnswer({answer:a,question:b}){document.getElementById("question").innerHTML=b,document.getElementById("answer").innerHTML="ANSWER: "+a,document.getElementById("pause").disabled=!0,showNextButton()}function showNextButton(){document.getElementById("next").classList.remove("d-none"),document.getElementById("next").disabled=!1,document.getElementById("skip").classList.add("d-none"),document.getElementById("skip").disabled=!0}function showSkipButton(){document.getElementById("skip").classList.remove("d-none"),document.getElementById("skip").disabled=!document.getElementById("toggle-skip").checked,document.getElementById("next").classList.add("d-none"),document.getElementById("next").disabled=!0}function sortPlayerListGroup(c=!0){const d=document.getElementById("player-list-group"),e=Array.from(d.children),f=11;e.sort((d,a)=>{const b=parseInt(document.getElementById("points-"+d.id.substring(f)).innerHTML),e=parseInt(document.getElementById("points-"+a.id.substring(f)).innerHTML);// if points are equal, sort alphabetically by username +if(b===e){const b=document.getElementById("username-"+d.id.substring(f)).innerHTML,e=document.getElementById("username-"+a.id.substring(f)).innerHTML;return c?b.localeCompare(e):e.localeCompare(b)}return c?e-b:b-e}).forEach(a=>{d.appendChild(a)})}function setCategories({alternateSubcategories:a,categories:b,subcategories:c,percentView:d,categoryPercents:e,username:f}){logEvent(f,"updated the categories"),categoryManager.import({categories:b,subcategories:c,alternateSubcategories:a,percentView:d,categoryPercents:e}),categoryManager.loadCategoryModal()}function setDifficulties({difficulties:a,username:b=void 0}){return b&&logEvent(b,0{const c=b.querySelector("input");a.includes(parseInt(c.value))?(c.checked=!0,b.classList.add("active")):(c.checked=!1,b.classList.remove("active"))}):void(startingDifficulties=a)}function setPacketNumbers({username:a,packetNumbers:b}){b=arrayToRange(b),logEvent(a,0{upsertPlayerItem(players[a],USER_ID,ownerId,socket,globalPublic)})}function updateQuestion({word:a}){"(*)"===a||(document.getElementById("question").innerHTML+=a+" ")}function updateTimerDisplay(a){const b=Math.floor(a/10);document.querySelector(".timer .face").innerText=b,document.querySelector(".timer .fraction").innerText="."+a%10}function vkInit({targetUsername:a,threshold:b}){logEvent(`A votekick has been started against user ${a} and needs ${b} votes to suceed.`)}function vkHandle({targetUsername:a,targetId:b}){USER_ID===b?(window.alert("You were vote kicked from this room by others."),setTimeout(()=>{window.location.replace("../")},100)):logEvent(a+" has been vote kicked from this room.")}document.getElementById("answer-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("answer-input").value;socket.send(JSON.stringify({type:"give-answer",givenAnswer:b}))}),document.getElementById("answer-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"give-answer-live-update",message:this.value}))}),document.getElementById("buzz").addEventListener("click",function(){this.blur(),audio.soundEffects&&audio.buzz.play(),socket.send(JSON.stringify({type:"buzz"})),socket.send(JSON.stringify({type:"give-answer-live-update",message:""}))}),document.getElementById("chat").addEventListener("click",function(){this.blur(),document.getElementById("chat-input-group").classList.remove("d-none"),document.getElementById("chat-input").focus(),socket.send(JSON.stringify({type:"chat-live-update",message:""}))}),document.getElementById("chat-form").addEventListener("submit",function(a){a.preventDefault(),a.stopPropagation();const b=document.getElementById("chat-input").value;document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:b}))}),document.getElementById("chat-input").addEventListener("input",function(){socket.send(JSON.stringify({type:"chat-live-update",message:this.value}))}),document.getElementById("clear-stats").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"clear-stats"}))}),document.getElementById("next").addEventListener("click",function(){switch(this.blur(),this.innerHTML){case"Start":socket.send(JSON.stringify({type:"start"}));break;case"Next":socket.send(JSON.stringify({type:"next"}))}}),document.getElementById("skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"skip"}))}),document.getElementById("packet-number").addEventListener("change",function(){const a=rangeToArray(this.value,maxPacketNumber);return a.some(a=>1>a||a>maxPacketNumber)?void document.getElementById("packet-number").classList.add("is-invalid"):void(document.getElementById("packet-number").classList.remove("is-invalid"),socket.send(JSON.stringify({type:"set-packet-numbers",packetNumbers:a})))}),document.getElementById("pause").addEventListener("click",function(){this.blur();const a=parseFloat(document.querySelector(".timer .face").innerText),b=parseFloat(document.querySelector(".timer .fraction").innerText);socket.send(JSON.stringify({type:"pause",pausedTime:10*(a+b)}))}),document.getElementById("reading-speed").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-reading-speed",readingSpeed:this.value}))}),document.getElementById("reading-speed").addEventListener("input",function(){document.getElementById("reading-speed-display").textContent=this.value}),document.getElementById("report-question-submit").addEventListener("click",function(){api.reportQuestion(document.getElementById("report-question-id").value,document.getElementById("report-question-reason").value,document.getElementById("report-question-description").value)}),document.getElementById("set-name").addEventListener("change",async function(){api.getSetList().includes(this.value)||0===this.value.length?this.classList.remove("is-invalid"):this.classList.add("is-invalid"),maxPacketNumber=await api.getNumPackets(this.value),document.getElementById("packet-number").value=""===this.value||0===maxPacketNumber?"":`1-${maxPacketNumber}`,socket.send(JSON.stringify({type:"set-set-name",setName:this.value,packetNumbers:rangeToArray(document.getElementById("packet-number").value)}))}),document.getElementById("strictness").addEventListener("change",function(){this.blur(),socket.send(JSON.stringify({type:"set-strictness",strictness:this.value}))}),document.getElementById("strictness").addEventListener("input",function(){document.getElementById("strictness-display").textContent=this.value}),document.getElementById("toggle-lock").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-lock",lock:this.checked}))}),document.getElementById("toggle-login-required").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-login-required",loginRequired:this.checked}))}),document.getElementById("toggle-powermark-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-powermark-only",powermarkOnly:this.checked}))}),document.getElementById("toggle-rebuzz").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-rebuzz",rebuzz:this.checked}))}),document.getElementById("toggle-skip").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-skip",skip:this.checked}))}),document.getElementById("toggle-select-by-set-name").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-select-by-set-name",setName:document.getElementById("set-name").value,selectBySetName:this.checked}))}),document.getElementById("toggle-settings").addEventListener("click",function(){this.blur(),document.getElementById("buttons").classList.toggle("col-lg-9"),document.getElementById("buttons").classList.toggle("col-lg-12"),document.getElementById("content").classList.toggle("col-lg-9"),document.getElementById("content").classList.toggle("col-lg-12"),document.getElementById("settings").classList.toggle("d-none"),document.getElementById("settings").classList.toggle("d-lg-none")}),document.getElementById("toggle-standard-only").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-standard-only",standardOnly:this.checked}))}),document.getElementById("toggle-timer").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-timer",timer:this.checked}))}),document.getElementById("toggle-public").addEventListener("click",function(){this.blur(),socket.send(JSON.stringify({type:"toggle-public",public:this.checked}))}),document.getElementById("username").addEventListener("change",function(){socket.send(JSON.stringify({type:"set-username",userId:USER_ID,username:this.value})),username=this.value,window.localStorage.setItem("multiplayer-username",username)}),document.getElementById("year-range-a").onchange=function(){const[a,b]=$("#slider").slider("values");if(b{if("Escape"===a.key&&"chat-input"===document.activeElement.id&&(document.getElementById("chat-input").value="",document.getElementById("chat-input-group").classList.add("d-none"),document.getElementById("chat-input").blur(),socket.send(JSON.stringify({type:"chat",message:""}))),!["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName))switch(a.key?.toLowerCase()){case" ":document.getElementById("buzz").click(),a.target===document.body&&a.preventDefault();break;case"e":return document.getElementById("toggle-settings").click();case"k":return document.getElementsByClassName("card-header-clickable")[0].click();case"p":return document.getElementById("pause").click();case"t":return document.getElementsByClassName("star-tossup")[0].click();case"y":return navigator.clipboard.writeText(tossup._id??"");case"n":case"s":document.getElementById("next").click(),document.getElementById("skip").click()}}),document.addEventListener("keypress",function(a){"Enter"===a.key&&a.target===document.body&&document.getElementById("chat").click()}),document.getElementById("username").value=username,ReactDOM.createRoot(document.getElementById("category-modal-root")).render(/*#__PURE__*/React.createElement(CategoryModal,{categoryManager:categoryManager,onClose:()=>{oldCategories!==JSON.stringify(categoryManager.export())&&socket.send(JSON.stringify({type:"set-categories",...categoryManager.export()})),oldCategories=JSON.stringify(categoryManager.export())}})),ReactDOM.createRoot(document.getElementById("difficulty-dropdown-root")).render(/*#__PURE__*/React.createElement(DifficultyDropdown,{startingDifficulties:startingDifficulties,onChange:()=>socket.send(JSON.stringify({type:"set-difficulties",difficulties:getDropdownValues("difficulties")}))})); \ No newline at end of file diff --git a/client/scripts/upsertPlayerItem.js b/client/scripts/upsertPlayerItem.js index e4566b8fa..7351fc7ce 100644 --- a/client/scripts/upsertPlayerItem.js +++ b/client/scripts/upsertPlayerItem.js @@ -47,15 +47,15 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu // Popover content playerItem.setAttribute('data-bs-title', username); playerItem.setAttribute('data-bs-content', ` -
      -
    • Powers${powers}
    • -
    • Tens${tens}
    • -
    • Negs${negs}
    • -
    • TUH${tuh}
    • -
    • Celerity${celerity.toFixed(3)}
    • -
    • Is Owner?${playerIsOwner ? 'Yes' : 'No'}
    • -
    - `); +
      +
    • Powers${powers}
    • +
    • Tens${tens}
    • +
    • Negs${negs}
    • +
    • TUH${tuh}
    • +
    • Celerity${celerity.toFixed(3)}
    • +
    • Is Owner?${playerIsOwner ? 'Yes' : 'No'}
    • +
    + `); document.getElementById('player-list-group').appendChild(playerItem); @@ -67,7 +67,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu banButton.innerText = 'Ban'; playerItem.appendChild(banButton); banButton.addEventListener('click', () => { - socket.send(JSON.stringify({ type: 'ban', ownerId, target_user: userId, targ_name: username })); + socket.send(JSON.stringify({ type: 'ban', targetId: userId, targetUsername: username })); }); } @@ -79,8 +79,8 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu vkButton.innerText = 'VK'; playerItem.appendChild(vkButton); vkButton.addEventListener('click', () => { - socket.send(JSON.stringify({ type: 'votekick-vote', target_user: userId, targ_name: username, send_id: USER_ID })); - socket.send(JSON.stringify({ type: 'votekick-init', target_user: userId, targ_name: username, send_id: USER_ID })); + socket.send(JSON.stringify({ type: 'votekick-vote', targetId: userId })); + socket.send(JSON.stringify({ type: 'votekick-init', targetId: userId })); vkButton.disabled = true; vkButton.innerText = 'Cooldown'; setTimeout(() => { @@ -98,7 +98,7 @@ export default function upsertPlayerItem (player, USER_ID, ownerId, socket, isPu muteButton.innerText = 'Mute'; playerItem.appendChild(muteButton); muteButton.addEventListener('click', () => { - socket.send(JSON.stringify({ type: 'mute-toggle', targetId: userId, sendingMuteId: USER_ID, muteStatus: muteButton.innerText })); + socket.send(JSON.stringify({ type: 'toggle-mute', targetId: userId, muteStatus: muteButton.innerText })); if (muteButton.innerText === 'Unmute') { muteButton.innerText = 'Mute'; } else { diff --git a/quizbowl/Player.js b/quizbowl/Player.js index ffdfab77c..e0ece4d81 100644 --- a/quizbowl/Player.js +++ b/quizbowl/Player.js @@ -4,7 +4,6 @@ class Player { this.MAX_USERNAME_LENGTH = MAX_USERNAME_LENGTH; this.username = ''; - this.owner = false; this.powers = 0; this.tens = 0; this.zeroes = 0; @@ -23,10 +22,6 @@ class Player { }; } - assignOwner () { - this.owner = true; - } - clearStats () { this.powers = 0; this.tens = 0; diff --git a/server/multiplayer/ServerTossupRoom.js b/server/multiplayer/ServerTossupRoom.js index 3c1f00ff5..9dcb8c05c 100644 --- a/server/multiplayer/ServerTossupRoom.js +++ b/server/multiplayer/ServerTossupRoom.js @@ -1,4 +1,5 @@ import ServerPlayer from './ServerPlayer.js'; +import Votekick from './VoteKick.js'; import { HEADER, ENDC, OKBLUE, OKGREEN } from '../bcolors.js'; import isAppropriateString from '../moderation/is-appropriate-string.js'; import insertTokensIntoHTML from '../../quizbowl/insert-tokens-into-html.js'; @@ -25,6 +26,7 @@ export default class ServerTossupRoom extends TossupRoom { this.bannedUserList = []; this.kickedUserList = []; this.votekickList = []; + this.lastVotekickTime = {}; this.rateLimiter = new RateLimit(50, 1000); this.rateLimitExceeded = new Set(); @@ -40,28 +42,27 @@ export default class ServerTossupRoom extends TossupRoom { async message (userId, message) { switch (message.type) { - case 'ban': return this.banUser(message.ownerId, message.target_user, message.targ_name); + case 'ban': return this.ban(userId, message); case 'chat': return this.chat(userId, message); case 'chat-live-update': return this.chatLiveUpdate(userId, message); case 'give-answer-live-update': return this.giveAnswerLiveUpdate(userId, message); - case 'mute-toggle': return this.toggleMute(message.targetId, message.sendingMuteId, message.muteStatus); case 'toggle-lock': return this.toggleLock(userId, message); case 'toggle-login-required': return this.toggleLoginRequired(userId, message); + case 'toggle-mute': return this.toggleMute(userId, message); case 'toggle-public': return this.togglePublic(userId, message); - case 'owner-id': return this.owner_id(this.ownerId); - case 'votekick-init': return this.votekickInit(message.target_user, message.targ_name, message.send_id); - case 'votekick-vote': return this.votekickVote(message.target_user, message.send_id); + case 'votekick-init': return this.votekickInit(userId, message); + case 'votekick-vote': return this.votekickVote(userId, message); default: super.message(userId, message); } } - banUser (ownerId, targetUser, targetUsername) { - console.log('Ban request recieved. Target ' + targetUser); - if (this.ownerId === ownerId) { - console.log('Checked, owner sent ban'); - this.emitMessage({ type: 'verified-ban', target: targetUser, targetUsername }); - this.bannedUserList.push(targetUser); - } + ban (userId, { targetId, targetUsername }) { + console.log('Ban request recieved. Target ' + targetId); + if (this.ownerId !== userId) { return; } + + console.log('Checked, owner sent ban'); + this.emitMessage({ type: 'confirm-ban', targetId, targetUsername }); + this.bannedUserList.push(targetId); } connection (socket, userId, username) { @@ -72,13 +73,15 @@ export default class ServerTossupRoom extends TossupRoom { this.players[userId].online = true; this.sockets[userId] = socket; username = this.players[userId].safelySetUsername(username); + if (this.bannedUserList.includes(userId)) { - console.log('Banned user ' + userId + ' (' + username + ') tried to join a room'); + console.log(`Banned user ${userId} (${username}) tried to join a room`); this.sendToSocket(userId, { type: 'enforcing-removal', removalType: 'ban' }); return; } + if (this.kickedUserList.includes(userId)) { - console.log('Kicked user ' + userId + ' (' + username + ') tried to join a room'); + console.log(`Kicked user ${userId} (${username}) tried to join a room`); this.sendToSocket(userId, { type: 'enforcing-removal', removalType: 'kick' }); return; } @@ -105,6 +108,7 @@ export default class ServerTossupRoom extends TossupRoom { type: 'connection-acknowledged', userId, + ownerId: this.ownerId, players: this.players, isPermanent: this.isPermanent, @@ -170,10 +174,6 @@ export default class ServerTossupRoom extends TossupRoom { super.next(userId, { type }); } - owner_id (id) { - this.emitMessage({ type: 'owner-check', id }); - } - setCategories (userId, { categories, subcategories, alternateSubcategories, percentView, categoryPercents }) { if (this.isPermanent) { return; } super.setCategories(userId, { categories, subcategories, alternateSubcategories, percentView, categoryPercents }); @@ -223,8 +223,8 @@ export default class ServerTossupRoom extends TossupRoom { this.emitMessage({ type: 'toggle-login-required', loginRequired, username }); } - toggleMute (targetId, senderId, muteStatus) { - this.sendToSocket(senderId, { type: 'mute-notice', targetId, muteStatus }); + toggleMute (userId, { targetId, muteStatus }) { + this.sendToSocket(userId, { type: 'mute-player', targetId, muteStatus }); } togglePublic (userId, { public: isPublic }) { @@ -252,90 +252,50 @@ export default class ServerTossupRoom extends TossupRoom { super.toggleTimer(userId, { timer }); } - votekickInit (targetId, targetName, sendingId) { - if (!this.lastVotekickTime) { - this.lastVotekickTime = {}; - } + votekickInit (userId, { targetId }) { + const targetUsername = this.players[targetId].username; const currentTime = Date.now(); - if (this.lastVotekickTime[sendingId] && (currentTime - this.lastVotekickTime[sendingId] < 90000)) { - console.log(`Votekick denied: ${sendingId} 90 second cooldown viol.`); + if (this.lastVotekickTime[userId] && (currentTime - this.lastVotekickTime[userId] < 90000)) { + console.log(`Votekick denied: ${userId} 90 second cooldown violation.`); return; } - this.lastVotekickTime[sendingId] = currentTime; + this.lastVotekickTime[userId] = currentTime; - let exists = false; - this.votekickList.forEach((votekick) => { - if (votekick.exists(targetId)) { - exists = true; - } - }); - if (exists) { - return; + for (const votekick of this.votekickList) { + if (votekick.exists(targetId)) { return; } } const threshold = Math.max((Object.keys(this.players).length) - 1, 0); - const votekick = new Votekick(targetName, targetId, [], threshold); - votekick.vote(sendingId); + const votekick = new Votekick(targetId, threshold, []); + votekick.vote(userId); if (votekick.check()) { - this.emitMessage({ type: 'successful-vk', targetName: votekick.targName, targetId: votekick.targId }); + this.emitMessage({ type: 'successful-vk', targetUsername, targetId }); this.kickedUserList.push(targetId); } else { this.votekickList.push(votekick); - this.emitMessage({ type: 'initiated-vk', targetName: votekick.targName, targetId: votekick.targId, threshold }); + this.emitMessage({ type: 'initiated-vk', targetUsername, threshold }); } - console.log(this.votekickList); } - votekickVote (targetUser, votingId) { + votekickVote (userId, { targetId }) { + const targetUsername = this.players[targetId].username; + let exists = false; let thisVotekick; - this.votekickList.forEach((votekick) => { - if (votekick.exists(targetUser)) { + for (const votekick of this.votekickList) { + if (votekick.exists(targetId)) { thisVotekick = votekick; exists = true; } - }); - if (!exists) { - return; } - thisVotekick.vote(votingId); - if (thisVotekick.check()) { - this.emitMessage({ type: 'successful-vk', targetName: thisVotekick.targName, targetId: thisVotekick.targId }); - this.kickedUserList.push(targetUser); - } - console.log(this.votekickList); - } -} - -class Votekick { - constructor (targName, targId, voted = [], threshold) { - this.targName = targName; - this.targId = targId; - this.voted = Array.isArray(voted) ? voted : []; - this.threshold = threshold; - } - - exists (givenId) { - if (this.targId === givenId) { - return true; - } else { - return false; - } - } - - vote (votingId) { - if (!this.voted.includes(votingId)) { - this.voted.push(votingId); - } - } + if (!exists) { return; } - check () { - if (this.voted.length >= this.threshold) { - return true; - } else { - return false; + thisVotekick.vote(userId); + if (thisVotekick.check()) { + this.emitMessage({ type: 'successful-vk', targetUsername, targetId }); + this.kickedUserList.push(targetId); } } } diff --git a/server/multiplayer/VoteKick.js b/server/multiplayer/VoteKick.js new file mode 100644 index 000000000..5c1f90d77 --- /dev/null +++ b/server/multiplayer/VoteKick.js @@ -0,0 +1,21 @@ +export default class Votekick { + constructor (targetId, threshold, voted = []) { + this.targetId = targetId; + this.voted = Array.isArray(voted) ? voted : []; + this.threshold = threshold; + } + + exists (givenId) { + return this.targetId === givenId; + } + + vote (votingId) { + if (!this.voted.includes(votingId)) { + this.voted.push(votingId); + } + } + + check () { + return this.voted.length >= this.threshold; + } +}