C&C:Online 新官网房间满人提示器

那些建房之后就把游戏切出去的人可以试试这个脚本,这个脚本将会在房间即将满人的时候播放声音。该该脚本通过挂钩 CNCOnline 网站的 serverinfo.js 函数来获得房间信息。

  1. // ==UserScript==
  2. // @name C&C:Online (Near) Full room notifier
  3. // @name:zh-CN C&C:Online 新官网房间满人提示器
  4. // @namespace https://github.com/lanyizi/C-C-Online-Website-hooks/
  5. // @version 0.1060001
  6. // @description A script for those game hosts who are AFK. It will play sound when the game is full or nearly full. It works by hooking some CNCOnline serverinfo.js functions.
  7. // @description:zh-CN 那些建房之后就把游戏切出去的人可以试试这个脚本,这个脚本将会在房间即将满人的时候播放声音。该该脚本通过挂钩 CNCOnline 网站的 serverinfo.js 函数来获得房间信息。
  8. // @author [RA3Bar]Lanyi
  9. // @match https://cnc-online.net/*
  10. // @grant none
  11. // @run-at document-end
  12. // @license GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt
  13. // ==/UserScript==
  14.  
  15. function main() {
  16.  
  17. window.anyNewStagingGames = function(newStaging) {
  18. let oldGames = window.oldStagingGames;
  19. let newGames = newStaging
  20. .filter(function(game) { return !game.map.startsWith("Co-Op "); })
  21. .map(function(game) { return game.host.id + game.title; });
  22. window.oldStagingGames = newGames;
  23. if(!oldGames) {
  24. return false;
  25. }
  26. for(let i = 0; i < newGames.length; ++i) {
  27. if(oldGames.indexOf(newGames[i]) == -1) {
  28. return true;
  29. }
  30. }
  31. return false;
  32. };
  33. window.playersChanged = function(host, newPlayers, isFull) {
  34. let newHost = host.nickanme;
  35. let oldHost = window.previousHost;
  36. let wasFull = window.previousWasFull;
  37. window.previousHost = newHost;
  38. window.previousWasFull = isFull;
  39. let oldPlayers = window.previousPlayers;
  40. let mapped = newPlayers.map(function(player) { return player.nickname; });
  41. window.previousPlayers = mapped;
  42. if(oldHost != newHost) {
  43. // reset to prevent false positives when joining new rooms in the future
  44. window.previousWasFull = true;
  45. return false;
  46. }
  47. if(oldPlayers === undefined) {
  48. return false;
  49. }
  50. if(oldPlayers.length != mapped.length) {
  51. console.log("players changed: length previous: " + oldPlayers.length + "; length now: " + mapped.length);
  52. return true;
  53. }
  54. for(let i = 0; i < mapped.length; ++i)
  55. {
  56. if(oldPlayers[i] != mapped[i]) {
  57. console.log("player changed: was" + oldPlayers[i] + "; now: " + mapped[i]);
  58. return true;
  59. }
  60. }
  61. if(!wasFull && isFull) {
  62. console.log("Full status changed from false to true");
  63. return true;
  64. }
  65. return false;
  66. };
  67. window.myPrefix = "RA3Bar_Lanyi_CNCOLWebsiteNotifier_";
  68. window.playerNameField = "PlayerNameField_";
  69. window.monitorStagingGameId = window.myPrefix + "monitorStagingGame";
  70.  
  71. for (let i = 0; i < gamenames.length; ++i) {
  72. window[myPrefix + playerNameField + gamenames[i]] = null;
  73. }
  74.  
  75. //from: http://s1download-universal-soundbank.com/wav/2838.wav
  76. window[myPrefix + "sound"] = new Audio("https://raw.githubusercontent.com/BSG-75/C-C-Online-Website-hooks/master/2838%5B2%5D.wav");
  77. function notifyPlayer() {
  78. window[myPrefix + "sound"].play();
  79. }
  80.  
  81. //https://stackoverflow.com/questions/5499078/fastest-method-to-escape-html-tags-as-html-entities
  82. function escapeHTMLTags(str) {
  83. return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  84. }
  85.  
  86. //onfocus
  87. window[myPrefix + "onMyFieldFocus"] = function(field) {
  88. let myFieldID = field.id;
  89. if(!window[myFieldID] || window[myFieldID].length == 0) {
  90. field.innerText = "";
  91. }
  92. };
  93.  
  94. //oninput
  95. window[myPrefix + "onMyFieldInput"] = function(field) {
  96. let myFieldID = field.id;
  97. window[myFieldID] = field.innerText.trim();
  98. };
  99.  
  100. let originalSetUserBarInfo = setUserbarInfo;
  101. let originalGetUserSection = getUserSection;
  102. let originalHandleJSON = handleJSON;
  103. setUserbarInfo = function($parent, response) {
  104. let result = originalSetUserBarInfo($parent, response);
  105. $parent.append("← Click game name to monitor your room!");
  106. };
  107. getUserSection = function(response, gamename) {
  108. let myFieldID = myPrefix + playerNameField + gamename;
  109. let myFieldStyle = "background: #A00060; margin-left: 1em; padding-left: 0.5em; padding-right: 0.5em;"
  110. let myFieldDefaultValue = "<Type your nickname here~>";
  111. let myFieldValue = myFieldDefaultValue;
  112. if(window[myFieldID] && window[myFieldID].length > 0) {
  113. myFieldValue = window[myFieldID];
  114. }
  115.  
  116. let attributes = "contenteditable = \"true\" id = \"" + myFieldID + "\" style = \"" + myFieldStyle + "\"";
  117. attributes += " onfocus = \"" + myPrefix + "onMyFieldFocus(this);" + "\" ";
  118. attributes += " oninput = \"" + myPrefix + "onMyFieldInput(this);" + "\" ";
  119. let myField = "<span " + attributes + ">" + escapeHTMLTags(myFieldValue) + "</span>";
  120.  
  121. let result = originalGetUserSection(response, gamename);
  122. result.find("h3").append(myField);
  123.  
  124. return result;
  125. };
  126. handleJSON = function(response, textStatus, jqXHR) {
  127. let result = originalHandleJSON(response, textStatus, jqXHR);
  128. for (let i = 0; i < gamenames.length; ++i) {
  129. let gamename = gamenames[i];
  130. let nickname = window[myPrefix + playerNameField + gamename];
  131. let games = response[gamename].games.staging;
  132. if(nickname) {
  133. for(let userNickname in response[gamename].users) {
  134. if(userNickname.toUpperCase() == nickname.toUpperCase()) {
  135. let inRoom = false;
  136. function roomChecker(game) {
  137. function nameChecker(x) {
  138. return x.nickname.toUpperCase() == userNickname.toUpperCase();
  139. }
  140. if(game.players.some(nameChecker)) {
  141. inRoom = true;
  142. }
  143. };
  144. response[gamename].games.staging.forEach(roomChecker);
  145. response[gamename].games.playing.forEach(roomChecker);
  146. if(!inRoom) {
  147. if(window.anyNewStagingGames(games)) {
  148. notifyPlayer();
  149. }
  150. }
  151. }
  152. }
  153. }
  154. $.each(games, function(i, game) {
  155. //if ( parseInt(game.numRealPlayers)+parseInt(game.numObservers) == parseInt(game.maxRealPlayers) ) {
  156. // $gameItem.addClass('full');
  157. //}
  158. if (game.players != 'error') {
  159. $.each(game.players, function(j, player) {
  160. if(nickname && player.nickname) {
  161. if(player.nickname.toUpperCase() == nickname.toUpperCase()) {
  162. let realPlayers = parseInt(game.numRealPlayers);
  163. let observers = parseInt(game.numObservers);
  164. let maxPlayers = parseInt(game.maxRealPlayers);
  165. let isFull = (realPlayers + observers) >= maxPlayers;
  166. if(realPlayers + observers >= maxPlayers * 0.5) {
  167. if(window.playersChanged(game.host, game.players, isFull)) {
  168. notifyPlayer();
  169. }
  170. }
  171. }
  172. }
  173. });
  174. }
  175. });
  176. }
  177. return result;
  178. };
  179. setInterval(getJSONInfo, 3000);
  180. function getJSONInfo() {
  181. $.ajax({
  182. url: json_url + "?callback=?",
  183. dataType: 'jsonp',
  184. data: { 'site': site },
  185. timeout: ajax_timeout,
  186. success: handleJSON,
  187. error: handleJSONError,
  188. });
  189. }
  190. }
  191.  
  192. (function() {
  193. 'use strict';
  194. // Your code here...
  195. let mainScript = document.createElement("script");
  196. mainScript.textContent = main.toString() + "\nmain();";
  197. document.body.appendChild(mainScript);
  198. })();