1 2const alertDialogUi = document.getElementById('alert'); 3const alertMessageUi = document.getElementById('alertMessage'); 4 5const homeUiWrapper = document.getElementById('homeWrapper'); 6 7const moderationUiWrapper = document.getElementById('moderationWrapper'); 8 9const messageUiWrapper = document.getElementById('messageWrapper'); 10const messageUiTitle = document.getElementById('messageTitle'); 11const messageUi = document.getElementById('message'); 12 13const roomInput = document.getElementById('room'); 14const passwordInput = document.getElementById('password'); 15 16 17const content = document.getElementById('content'); 18 19const PLAYER_ID_KEY = 'WTD_PLAYER_ID'; 20const MODERATOR_KEY = 'MODERATOR'; 21 22const MAX_RECONNECT_TRIES = 10; 23const RECONNECT_TIMEOUT = 1000; 24 25var modWrappers = [ 26 document.getElementById('mod0'), 27 document.getElementById('mod1'), 28 document.getElementById('mod2'), 29 document.getElementById('mod3'), 30 document.getElementById('mod4'), 31 document.getElementById('mod5'), 32]; 33 34var modButtons = [ 35 document.getElementById('modButton0'), 36 document.getElementById('modButton1'), 37 document.getElementById('modButton2'), 38 document.getElementById('modButton3'), 39 document.getElementById('modButton4'), 40 document.getElementById('modButton5'), 41]; 42 43var usernameDivs = [ 44 document.getElementById('modUsername0'), 45 document.getElementById('modUsername1'), 46 document.getElementById('modUsername2'), 47 document.getElementById('modUsername3'), 48 document.getElementById('modUsername4'), 49 document.getElementById('modUsername5'), 50] 51 52 53if(localStorage.getItem('mod_pw') != null) 54 passwordInput.value = localStorage.getItem('mod_pw'); 55 56if(localStorage.getItem('room') != null) 57 roomInput.value = localStorage.getItem('room'); 58 59var _playerInternalId = localStorage.getItem('moderator_player_id'); 60var _roomName = ''; 61var _password = ''; 62var _connected = false; 63var _joinRoomAfterConnect = false; 64var _reconnectCount = 0; 65var _tryingToReconnect = false; 66var _checkReconnectOnVisible = false; 67var _currentSubmissions = []; 68 69 70let _wakeLock = null; 71let _waklockSupported = 'wakeLock' in navigator; 72 73 74var client = new Photon.LoadBalancing.LoadBalancingClient( 75 window.location.href.startsWith('https') ? Photon.ConnectionProtocol.Wss : Photon.ConnectionProtocol.Ws, 76 'ccced71e-9a4e-4c84-a70d-b78576965328', 77 'v0.1_local_play_2.23' 78); 79 80function updateDisplay(displayType){ 81 82 if(displayType == null){ 83 content.style.display = 'none'; 84 return; 85 } 86 87 content.style.display = 'block'; 88 89 if(displayType == 'home') { 90 releaseWakeLock(); 91 } 92 93 switch(displayType){ 94 case 'message': 95 case 'start': 96 case 'cancel': 97 case 'next_game': 98 case 'intro': 99 messageUiTitle.style.display = 'block' 100 break; 101 default: 102 messageUiTitle.style.display = 'none'; 103 break; 104 } 105 106 switch(displayType){ 107 case 'message': 108 case 'intro': 109 messageUiWrapper.style.display = 'block' 110 break; 111 default: 112 messageUiWrapper.style.display = 'none'; 113 break; 114 } 115 116 homeUiWrapper.style.display = displayType == 'home' ? 'block' : 'none'; 117 moderationUiWrapper.style.display = displayType == 'moderate' ? 'block' : 'none'; 118 119 setTimeout(scrollToTop, 0); 120 121} 122 123function scrollToTop(){ 124 document.documentElement.scrollTop = 0; 125} 126 127function initDisplay(){ 128 _joinRoomAfterConnect = false; 129 updateDisplay('home'); 130} 131 132initDisplay(); 133 134 135function createGuid() { 136 function _p8(s) { 137 let p = (Math.random().toString(16)+"000000000").substr(2,8); 138 return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ; 139 } 140 return _p8() + _p8(true) + _p8(true) + _p8(); 141} 142 143function setGenericMessage(text){ 144 messageUi.innerText = text; 145 updateDisplay('message'); 146} 147 148 149function closeAlert(){ 150 alertDialogUi.classList.remove('active'); 151} 152 153function setAlert(message){ 154 if(message == null || message == ''){ 155 closeAlert(); 156 return; 157 } 158 alertMessageUi.innerText = message; 159 alertDialogUi.classList.add('active'); 160} 161 162 163function joinGame(e){ 164 165 _checkReconnectOnVisible = false; 166 _tryingToReconnect = false; 167 168 _roomName = roomInput.value.trim().toUpperCase(); 169 if(_roomName.length == 0){ 170 setAlert("Please enter a room."); 171 return false; 172 } 173 174 175 localStorage.setItem('room', _roomName); 176 177 _password = passwordInput.value.trim().toUpperCase(); 178 179 if(_password.length == 0){ 180 setAlert('Please enter a password.'); 181 return false;
182 } 183 184 localStorage.setItem('mod_pw', _password); 185 186 //reset the actor 187 client._myActor = new Photon.LoadBalancing.Actor(createGuid(), -1, true); 188 closeAlert(); 189 190 if(_playerInternalId == null) { 191 _playerInternalId = createGuid() + '_' + Date.now(); 192 localStorage.setItem('player_id', _playerInternalId); 193 } 194 195 client.myActor().setCustomProperty(PLAYER_ID_KEY, _playerInternalId); 196 client.myActor().setCustomProperty(MODERATOR_KEY, 'true'); 197 198 setGenericMessage('Connecting...') 199 200 if(!_connected){ 201 connectToServerAndJoinRoom(); 202 } 203 else{ 204 joinRoom(); 205 } 206 207 return false; 208 209} 210 211function connectToServerAndJoinRoom(){ 212 _joinRoomAfterConnect = true; 213 client.connectToRegionMaster('us'); 214} 215 216function joinRoom(){ 217 closeAlert(); 218 client.joinRoom(_roomName); 219} 220 221function disconnect(){ 222 _connected = false; 223 224 if(client.isJoinedToRoom() || client.isConnectedToMaster()) 225 client.disconnect(); 226 else 227 initDisplay(); 228} 229 230 231const WaitingForPlayersToJoinState = 0; 232const PlayingVideoToDubState = 1; 233const UserSubmittingDubsState = 2; 234const PlayingBackSubmittedDubsState = 3; 235const VotingOnDubsState = 4; 236const DisplayingVotingResultsState = 5; 237const WaitingForNextGameState = 6; 238const PlayingInstructionsGameState = 7; 239const NextRoundState = 8; 240 241function raiseModerationEvent(eventType, eventData){ 242 243 if(eventData == null) 244 eventData = {} 245 246 eventData['password'] = _password; 247 eventData['playerId'] = _playerInternalId; 248 249 250 const data = { 251 'eventType': eventType, 252 'json': eventData != null ? JSON.stringify(eventData) : null, 253 }; 254 255 client.raiseEvent( 256 ModerationEvent, 257 JSON.stringify(data), 258 {'receivers': Photon.LoadBalancing.Constants.ReceiverGroup.MasterClient} 259 ); 260 261} 262 263 264 265client.onJoinRoom = function(createdByMe){ 266 267 raiseModerationEvent(ModerationVerifyPasswordEvent, null); 268 269 _checkReconnectOnVisible = false; 270 createWakeLock(); 271 272}; 273 274 275 276client.onActorJoin = function(actor) { 277 278} 279 280client.onActorLeave = function(actor, cleanup){ 281 282 //already disconnected so who cares 283 if(!_connected) 284 return; 285 286 //server gone 287 //only the server should ever by actor number 1 288 if(actor.getCustomProperty(PLAYER_ID_KEY) == 'SERVER' || actor.actorNr == 1){ 289 setAlert('Lost Connection to Host'); 290 disconnect(); 291 return; 292 } 293 294} 295 296 297client.onStateChange = function(state){ 298 switch(state){ 299 case Photon.LoadBalancing.LoadBalancingClient.State.ConnectedToMaster: 300 _connected = true; 301 break; 302 case Photon.LoadBalancing.LoadBalancingClient.State.JoinedLobby: 303 _connected = true; 304 305 if(_joinRoomAfterConnect){ 306 _joinRoomAfterConnect = false; 307 joinRoom(); 308 } 309 break; 310 311 case Photon.LoadBalancing.LoadBalancingClient.State.Disconnected: 312 //if false, it was an intentional disconnect 313 if(_connected){ 314 315 _checkReconnectOnVisible = document.visibilityState !== 'visible'; 316 if(_checkReconnectOnVisible) 317 return; 318 319 setAlert('Lost connection to server'); 320 _connected = false; 321 } 322 323 _checkReconnectOnVisible = false; 324 _joinRoomAfterConnect = false; 325 initDisplay(); 326 327 break; 328 329 } 330}; 331 332function alphaNumericOnly(text){ 333 text.value = text.value.replace( /[^a-zA-Z0-9 ]/gm, ''); 334} 335 336 337document.onkeydown = function(e){ 338 e = e || window.event; 339 if(e.keyCode == 27){ 340 closeOverlays(); 341 } 342} 343 344roomInput.addEventListener( "input", event => {alphaNumericOnly(roomInput);}, false); 345roomInput.addEventListener( "paste", event => {setTimeout(function(){alphaNumericOnly(roomInput);}, 0)}, false); 346passwordInput.addEventListener( "input", event => {alphaNumericOnly(passwordInput);}, false); 347passwordInput.addEventListener( "paste", event => {setTimeout(function(){alphaNumericOnly(passwordInput);}, 0)}, false); 348 349 350client.onError = function(errorCode, errorMsg){ 351 352 353 //game server disconnected 354 if(errorCode == 1003 && _connected){ 355 setAlert("Lost connection to host. Reconnecting..."); 356 _reconnectCount = 0; 357 attemptToReconnect(); 358 return; 359 } 360 361 //3004 name server peer timeout 362 if(errorCode == 3004){ 363 if(_tryingToReconnect){ 364 setTimeout(attemptToReconnect, RECONNECT_TIMEOUT); 365 }else{ 366 let port = getPortFromUrl(client.masterServerAddress, '19093'); 367 setAlert("Unable to connect to host. If this happens repeatedly, ensure that port " + port + " is not blocked on your network's firewall"); 368 initDisplay(); 369 } 370 } 371 372 //Master peer timeout, potentially due to firewall 373 if(errorCode == 1004){ 374 let port = getPortFromUrl(client.masterServerAddress, '19090'); 375 if(client.masterServerAddress != null && client.masterServerAddress.lastIndexOf(':') > 0){ 376 port = client.masterServerAddress.substring(client.masterServerAddress.lastIndexOf(':') + 1); 377 } 378 setAlert("Unable to connect to host. If this happens repeatedly, ensure that port " + port + " is not blocked on your network's firewall."); 379 initDisplay(); 380 } 381} 382 383function getPortFromUrl(url, defaultValue=''){ 384 if(url == null || url.lastIndexOf(':') <= 0) 385 return defaultValue; 386 387 return url.substring(url.lastIndexOf(':') + 1); 388} 389 390function attemptToReconnect(){ 391 setGenericMessage("Reconnecting..."); 392 _tryingToReconnect = true; 393 _reconnectCount++; 394 395 if(_reconnectCount > MAX_RECONNECT_TRIES){ 396 _connected = false;
397 _checkReconnectOnVisible = false; 398 client.disconnect(); 399 initDisplay(); 400 setAlert('Reconnecting to host failed.'); 401 return; 402 } 403 404 connectToServerAndJoinRoom(); 405 406 407} 408 409client.onOperationResponse = function(errorCode, errorMsg, code, content){ 410 if(errorCode){ 411 // game does not exist 412 if(errorCode == 32758){ 413 setAlert('Room "' + _roomName + '" not found.'); 414 disconnect(); 415 initDisplay(); 416 }else if(errorCode == 32765){ 417 setAlert('That game is full.'); 418 disconnect(); 419 initDisplay(); 420 } 421 } 422} 423 424 425const EnterDubTimeEvent = 1; 426const DubSubmittedEvent = 2; 427const PlayingBackDubsEvent = 3; 428const StreamingClientEvent = 150; 429const ModerationEvent = 151; 430 431 432client.onEvent = function(code, content, actorNr){ 433 434 switch(code){ 435 case StreamingClientEvent: 436 handleStreamingClientEvent(content); 437 break; 438 case ModerationEvent: 439 handleModerationEvent(content); 440 break; 441 case PlayingBackDubsEvent: 442 _currentSubmissions = []; 443 updateCurrentSubmissionsDisplay(); 444 break; 445 case EnterDubTimeEvent: 446 _currentSubmissions = []; 447 updateCurrentSubmissionsDisplay(); 448 break; 449 case DubSubmittedEvent: 450 451 let submission = JSON.parse(content); 452 let dedupe = []; 453 for(let x = 0; x<_currentSubmissions.length; x++){ 454 if(_currentSubmissions[x]['playerID'] == submission['playerID']) 455 continue; 456 dedupe.push(_currentSubmissions[x]); 457 } 458 459 dedupe.push(submission); 460 _currentSubmissions = dedupe; 461 updateCurrentSubmissionsDisplay(); 462 break; 463 } 464} 465 466const ModerationNotEnabledEvent = 0; 467const ModerationVerifyPasswordEvent = 1; 468const ModerationInvalidPasswordEvent = 2; 469const ModerationModExistsEvent = 3; 470const ModerationValidLoginEvent = 4; 471const ModerationNoModeratorPickARiffEvent = 5; 472const ModerationApprovalSubmittedEvent = 6; 473 474 475function handleModerationEvent(content){ 476 477 let event = JSON.parse(content); 478 let eventData = event['json'] != null && event['json'].length > 0 ? JSON.parse(event['json']) : {}; 479 480 switch(event['eventType']){ 481 case ModerationNotEnabledEvent: 482 disconnect(); 483 setAlert('Moderation is not enabled in this game.'); 484 break; 485 case ModerationInvalidPasswordEvent: 486 if(_playerInternalId != eventData['playerId']) 487 return; 488 489 disconnect(); 490 setAlert('Invalid moderation password.'); 491 break; 492 case ModerationModExistsEvent: 493 494 if(_playerInternalId != eventData['playerId']) 495 return; 496 497 disconnect(); 498 setAlert('A moderator is already connected to this game.'); 499 break; 500 case ModerationValidLoginEvent: 501 502 if(_playerInternalId != eventData['playerId']) 503 return; 504 505 checkGameStateOnJoin(); 506 break; 507 case ModerationNoModeratorPickARiffEvent: 508 disconnect(); 509 setAlert('Pick-A-Riff mode does not currently support moderation.'); 510 break; 511 512 } 513 514} 515 516 517const StreamingClientPickARiffSubmissionEvent = 8; 518 519function handleStreamingClientEvent(content){ 520 let event = JSON.parse(content); 521 let eventData = event['json'] != null && event['json'].length > 0 ? JSON.parse(event['json']) : {}; 522 523 switch(event['eventType']){ 524 case StreamingClientPickARiffSubmissionEvent: 525 let submission = eventData['sub'] 526 527 let dedupe = []; 528 for(let x = 0; x<_currentSubmissions.length; x++){ 529 if(_currentSubmissions[x]['playerID'] == submission['playerID']) 530 continue; 531 dedupe.push(_currentSubmissions[x]); 532 } 533 534 dedupe.push(submission); 535 _currentSubmissions = dedupe; 536 updateCurrentSubmissionsDisplay(); 537 break; 538 } 539} 540 541function checkGameStateOnJoin(){ 542 543 544 let gameState = client.myRoom().getCustomPropertyOrElse('game_state', 0); 545 if(gameState != UserSubmittingDubsState){ 546 _currentSubmissions = []; 547 updateCurrentSubmissionsDisplay(); 548 return; 549 } 550 551 552 _currentSubmissions = JSON.parse(client.myRoom().getCustomPropertyOrElse('current_submissions', '{"Items":[]}'))['Items']; 553 554 updateCurrentSubmissionsDisplay(); 555} 556 557function updateCurrentSubmissionsDisplay(){ 558 if(_currentSubmissions.length == 0) { 559 setGenericMessage('Waiting for submissions.'); 560 return; 561 } 562 563 for(let x = 0; x<modWrappers.length; x++){ 564 if(x >= _currentSubmissions.length){ 565 modWrappers[x].style.display = 'none';
566 continue; 567 } 568 569 modWrappers[x].style.display = 'block'; 570 571 modButtons[x].innerText = _currentSubmissions[x].dub; 572 573 let userName = getUsernameForPlayerId(_currentSubmissions[x].playerID); 574 575 if(userName != null){ 576 usernameDivs[x].innerText = ' - ' + userName; 577 usernameDivs[x].style.display = 'block'; 578 }else{ 579 usernameDivs[x].style.display = 'none'; 580 } 581 582 } 583 584 updateDisplay('moderate'); 585 586} 587 588function getUsernameForPlayerId(playerID){ 589 590 if(playerID == 'TWITCH_CHAT') 591 return 'Twitch Chat'; 592 593 for(let x = 0; x<client.actorsArray.length; x++){ 594 let actor = client.actorsArray[x]; 595 if(actor.getCustomPropertyOrElse('WTD_PLAYER_ID') == playerID) 596 return actor.name 597 } 598 599 return null; 600} 601 602 603function approveAll(){ 604 while(_currentSubmissions.length > 0){ 605 approveRiff(0); 606 } 607 _currentSubmissions = []; 608 updateCurrentSubmissionsDisplay(); 609} 610 611function rejectRiff(index){ 612 submitApproval(_currentSubmissions[index], true); 613 614 _currentSubmissions.splice(index, 1); 615 updateCurrentSubmissionsDisplay(); 616} 617 618function approveRiff(index){ 619 submitApproval(_currentSubmissions[index], false); 620 621 _currentSubmissions.splice(index, 1); 622 updateCurrentSubmissionsDisplay(); 623} 624 625function submitApproval(riff, rejected){ 626 627 const data = { 628 'rejected': rejected, 629 'playerID': riff['playerID'], 630 'id': riff['id'], 631 'password': _password, 632 }; 633 634 raiseModerationEvent(ModerationApprovalSubmittedEvent, data); 635} 636 637 638 639//wake lock 640async function createWakeLock(){ 641 642 if(!_waklockSupported) { 643 return; 644 } 645 try { 646 _wakeLock = await navigator.wakeLock.request('screen'); 647 648 _wakeLock.addEventListener('release', () => { 649 // the wake lock has been released 650 _wakeLock = null; 651 }); 652 } catch (err) { 653 } 654} 655 656function releaseWakeLock(){ 657 if(_wakeLock != null) 658 _wakeLock.release(); 659} 660 661document.addEventListener('visibilitychange', () => { 662 if (client.isJoinedToRoom() && document.visibilityState === 'visible') { 663 createWakeLock(); 664 } 665 666 667 //attempt to 668 if(document.visibilityState === 'hidden'){ 669 _checkReconnectOnVisible = client.isJoinedToRoom(); 670 } else if(document.visibilityState === 'visible'){ 671 if(_checkReconnectOnVisible){ 672 //nothing to do because still connected 673 if(client.isJoinedToRoom()) 674 return; 675 676 client.disconnect(); 677 attemptToReconnect(); 678 return; 679 } 680 681 disconnect(); 682 } 683}); 684 685
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.