Bonk Host

Makes hosting rooms in bonk.io better

目前为 2022-01-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Bonk Host
  3. // @version 1.7
  4. // @author Salama
  5. // @description Makes hosting rooms in bonk.io better
  6. // @match https://bonk.io/gameframe-release.html
  7. // @run-at document-start
  8. // @grant none
  9. // @supportURL https://discord.gg/Dj6usq7ww3
  10. // @namespace https://greasyfork.org/users/824888
  11. // ==/UserScript==
  12.  
  13. // for use as a userscript ensure you have Excigma's code injector userscript
  14. // https://greasyfork.org/en/scripts/433861-code-injector-bonk-io
  15.  
  16. let injector = (str) => {
  17. let newStr = str;
  18. window.bonkHost = {};
  19. window.bonkHost.playerManagement = {};
  20. window.bonkHost.freejoin = false;
  21. window.bonkHost.playerCount = 0;
  22. window.bonkHost.scores = [];
  23. window.bonkHost.startGameFunction = () => {return;};
  24.  
  25. let hostPlayerMenu = document.createElement('div');
  26. document.getElementById('pagecontainer').appendChild(hostPlayerMenu);
  27. hostPlayerMenu.outerHTML = `
  28. <div class="windowShadow" id="hostPlayerMenu"
  29. style="
  30. background-color: #cfd8cd;
  31. width: calc(35.2vw - 400px);
  32. min-width: 154px;
  33. max-width: 260px;
  34. height: 551px;
  35. position: absolute;
  36. left: 1%;
  37. top: calc(20% + 60px);
  38. border-radius: 7px;
  39. display: none">
  40. <div class="newbonklobby_boxtop newbonklobby_boxtop_classic" style="background-color: #009688;">Player List</div>
  41. <div id="hostPlayerMenuBox" class="newbonklobby_elementcontainer"></div>
  42. <div id="hostPlayerMenuControls" style="position:absolute;bottom:0;width:100%">
  43. <table>
  44. <tbody>
  45. <tr style="background-color: rgba(58, 58, 58, 0.07);">
  46. <td style="padding-left: 10px;" class="mapeditor_rightbox_table">
  47. Teamlock
  48. </td>
  49. <td>
  50. <input type="checkbox" id="hostPlayerMenuTeamlock">
  51. </td>
  52. </tr>
  53. <tr style="background-color: rgba(58, 58, 58, 0.07);">
  54. <td style="padding-left: 10px;" class="mapeditor_rightbox_table">
  55. Freejoin
  56. </td>
  57. <td>
  58. <input type="checkbox" id="hostPlayerMenuFreejoin">
  59. </td>
  60. </tr>
  61. <tr style="background-color: rgba(58, 58, 58, 0.07);">
  62. <td style="padding-left: 10px;" class="mapeditor_rightbox_table">
  63. Keep scores
  64. </td>
  65. <td>
  66. <input type="checkbox" id="hostPlayerMenuKeepScores">
  67. </td>
  68. </tr>
  69. </tbody>
  70. </table>
  71. <div class="newbonklobby_settings_button brownButton brownButton_classic buttonShadow" style="width:100%" id="hostPlayerMenuRestartButton">
  72. RESTART GAME
  73. </div>
  74. </div>
  75. </div>`;
  76.  
  77. let CUSTOM_COMMANDS = `
  78. if(I8H[5][0] == "/hhelp") {
  79. u6H[29].showStatusMessage("/balance * -100 to 100 -- Balances everyone","#cc3333",false);
  80. u6H[29].showStatusMessage("/balanceall -100 to 100 -- Balances everyone","#cc3333",false);
  81. u6H[29].showStatusMessage("/start -- Starts the game","#cc3333",false);
  82. u6H[29].showStatusMessage("/freejoin on/off -- Lets people join during the game","#cc3333",false);
  83. }
  84. else if(I8H[5][0] == "/start") {
  85. window.bonkHost.startGame();
  86. }
  87. else if(I8H[5][0] == "/freejoin") {
  88. if(["true", "on", "yes", "enable"].includes(I8H[5][1])) {
  89. window.bonkHost.freejoin = true;
  90. F5S("* Freejoin on","#cc3333",true);
  91. }
  92. else if(["false", "off", "no", "disable"].includes(I8H[5][1])) {
  93. window.bonkHost.freejoin = false;
  94. F5S("* Freejoin off","#cc3333",true);
  95. }
  96. else if(I8H[5].length == 1) {
  97. window.bonkHost.freejoin = !window.bonkHost.freejoin;
  98. F5S("* Freejoin " + (window.bonkHost.freejoin ? "on" : "off"),"#cc3333",true);
  99. }
  100. document.getElementById('hostPlayerMenuFreejoin').checked = window.bonkHost.freejoin;
  101. }
  102. `;
  103.  
  104. let BALANCE_ALL_MESSAGE = `
  105. if(I8H[67] == -2) {
  106. u6H[29].showStatusMessage("* " + "Everyone" + " has had their buff/nerf set to " + I8H[32], "#cc3333", false);
  107. }
  108. else if(I8H[32] == 0)
  109. `;
  110.  
  111. let BALANCE_SELECTION = `
  112.  
  113. u6H[36].bal[I8H[17]] = I8H[32];
  114. u6H[11].sendBalance(I8H[17], I8H[32]);
  115. if (u6H[29]) {
  116. u6H[29].updatePlayers();
  117. }
  118. I8H[67]=-2;
  119. if (u6H[44][I8H[17]].userName.toLowerCase() == I8H[7].toLowerCase()) {
  120. I8H[67] = I8H[17];
  121. break;
  122. }
  123. `;
  124.  
  125. let SUGGESTION_MODE_BUTTON = `
  126. let space = document.createElement("span");
  127. space.classList.add("newbonklobby_mapsuggest_high");
  128. space.appendChild(document.createTextNode(" "));
  129.  
  130. let smb = document.createElement("span");
  131. smb.classList.add("newbonklobby_mapsuggest_high");
  132. smb.classList.add("newbonklobby_chat_link");
  133. smb.style.color="#ff0000";
  134. v2k[79].setButtonSounds([smb]);
  135.  
  136. smb.onclick = () => {
  137. d9G[73].onclick();
  138. window.bonkSetMode(w3G[2][d9G[73].suggestID].m.mo);
  139. };
  140. `;
  141.  
  142. let APPEND_SUGGESTION_MODE_BUTTON = `
  143. if(!!v2k[10].modes[w3G[2][d9G[73].suggestID].m.mo]) {
  144. d9G[8].appendChild(space);
  145. smb.appendChild(document.createTextNode("[" + v2k[10].modes[w3G[2][d9G[73].suggestID].m.mo].lobbyName + "]"));
  146. d9G[8].appendChild(smb);
  147. }
  148. `;
  149.  
  150. let modeStuff = newStr.match(
  151. new RegExp(
  152. "(var .{2,4}=\\[arguments\\];.{2,4}\\[\\d{1,2}\\]=.{2,5};).{1,300}\
  153. \\+\\+;\
  154. if.{5,60}=0;\\}\
  155. (.{5,50}=.{5,50})\
  156. \\[.{2,4}\\[\\d{1,4}\\]\\];\
  157. (.{5,200}=true.{5,200}\\(\\);)\
  158. \\}\\}"
  159. )
  160. );
  161. // 1 is var m7p = [arguments]; m7p[4] = y3uu;
  162. // 2 is G7p[0][2]["mo"] = P1R[43]["lobbyModes"]
  163. // 3 is code that updates the mode
  164. let modeVar =
  165. modeStuff[2].split("=")[0].match(/.{2,4}\[\d{1,2}\]\[\d{1,2}\]/g)[0] +
  166. `["mo"]`;
  167. let modesObject =
  168. modeStuff[2].split("=")[1].match(/.{2,4}\[\d{2,4}\]/g)[0] + `["modes"]`;
  169.  
  170. window.modeDropdownCreated = false;
  171. window.createModeDropdown = () => {
  172. if (window.modeDropdownCreated) return;
  173. window.modeDropdownCreated = true;
  174. const dropdown = document.createElement("div");
  175. dropdown.classList = "dropdown-container";
  176. const mds = dropdown.style;
  177. mds.color = "#ffffff";
  178. mds.position = "absolute";
  179. mds.right = "15px";
  180. mds.bottom = "55px";
  181. mds.width = "116px";
  182. mds.height = "30px";
  183. mds.display = "flex";
  184. mds.textAlign = "center";
  185. mds.flexDirection = "column-reverse";
  186.  
  187. document.getElementById("newbonklobby_modebutton").remove();
  188. title = document.createElement("div");
  189. title.classList = "dropdown-title dropdown_classic";
  190. title.innerText = "MODE";
  191. title.style.fontSize = "18px";
  192. title.id = "newbonklobby_modebutton";
  193. title.style.position = "unset";
  194. dropdown.appendChild(title);
  195.  
  196. const options = [];
  197. let dropdownOpen = false;
  198.  
  199. function toggleVisibility(e) {
  200. dropdownOpen = !dropdownOpen;
  201. for (const o of options) {
  202. o.style.visibility = dropdownOpen ? "" : "hidden";
  203. }
  204. e.stopImmediatePropagation();
  205. }
  206.  
  207. for (const mode of Object.keys(window.bonkModesObject)) {
  208. const option = document.createElement("div");
  209. option.classList = "dropdown-option dropdown_classic";
  210. option.style.display = "block";
  211. option.style.visibility = "hidden";
  212. option.style.fontSize = "15px";
  213. option.innerText = window.bonkModesObject[mode].lobbyName;
  214. option.onclick = (e) => {
  215. window.bonkSetMode(mode);
  216. toggleVisibility(e);
  217. };
  218. options.push(option);
  219. dropdown.appendChild(option);
  220. }
  221.  
  222. title.addEventListener("click", toggleVisibility);
  223.  
  224. document.getElementById("newbonklobby_settingsbox").appendChild(dropdown);
  225. };
  226.  
  227. let PLAYER_CLICK_MENU = `
  228. if(playerEntry) {
  229. playerEntry.parentNode.appendChild(m9G[6]);
  230. }
  231. else` + " ";
  232.  
  233. let KEEP_SCORES = `
  234. if(window.bonkHost.scores.length > 0 && document.getElementById('hostPlayerMenuKeepScores').checked) {
  235. k7k[1].scores = window.bonkHost.scores;
  236. }
  237. else` + " ";
  238.  
  239. document.getElementById('hostPlayerMenuFreejoin').addEventListener('change', (e) => {
  240. window.bonkHost.freejoin = e.target.checked;
  241. });
  242.  
  243. document.getElementById('hostPlayerMenuTeamlock').addEventListener('change', () => {
  244. document.getElementById('newbonklobby_teamlockbutton').onclick();
  245. });
  246.  
  247. window.bonkHost.playerManagement.addPlayer = (playerEntry, info) => {
  248. let newPlayerEntry = playerEntry.cloneNode(true);
  249. newPlayerEntry.classList.remove('newbonklobby_playerentry_half');
  250. newPlayerEntry.getElementsByClassName("newbonklobby_playerentry_ping")[0].remove();
  251. newPlayerEntry.getElementsByClassName("newbonklobby_playerentry_host")[0].remove();
  252. newPlayerEntry.onclick = playerEntry.onclick;
  253. if(info.team == 0) {
  254. newPlayerEntry.style.filter = "opacity(0.4)";
  255. }
  256. hostPlayerMenuBox.appendChild(newPlayerEntry);
  257. }
  258. window.bonkHost.playerManagement.removePlayer = (playerEntry) => {
  259. if((foundPlayerEntry = window.bonkHost.playerManagement.getPlayer(playerEntry)) && foundPlayerEntry)
  260. hostPlayerMenuBox.removeChild(foundPlayerEntry);
  261. }
  262.  
  263. window.bonkHost.playerManagement.show = () => {
  264. if(window.bonkHost.menuFunctions.visible) return;
  265. parent.document.getElementById("adboxverticalleftCurse").style.display = "none";
  266. document.getElementById('hostPlayerMenu').style.removeProperty("display");
  267. }
  268.  
  269. window.bonkHost.playerManagement.hide = () => {
  270. document.getElementById('hostPlayerMenu').style.display = "none";
  271. parent.document.getElementById("adboxverticalleftCurse").style.removeProperty("display");
  272. }
  273.  
  274. window.bonkHost.playerManagement.getPlayer = (playerEntry, exact = false) => {
  275. if (exact) {
  276. let child = [...hostPlayerMenuBox.children].indexOf(playerEntry);
  277. if(child) return hostPlayerMenuBox.children[child];
  278. }
  279. for(let child of hostPlayerMenuBox.children) {
  280. if(child.children[1].innerText == playerEntry.children[1].innerText
  281. && child.children[1].innerText == playerEntry.children[1].innerText) {
  282. return child;
  283. }
  284. }
  285. return false;
  286. }
  287.  
  288. window.bonkHost.playerManagement.movePlayer = (playerID, playerCount, team) => {
  289. window.bonkHost.menuFunctions.visible = true;
  290. if(team > 0)
  291. window.bonkHost.bonkHandlers.hostHandlePlayerJoined(playerID, playerCount, team);
  292. else
  293. window.bonkHost.bonkHandlers.hostHandlePlayerLeft(playerID);
  294. window.bonkHost.menuFunctions.updatePlayers();
  295. }
  296.  
  297. window.bonkHost.startGame = () => {
  298. window.bonkHost.startGameFunction();
  299. }//Remove round limit
  300. document.getElementById('newbonklobby_roundsinput').removeAttribute("maxlength");
  301. newStr = newStr.replace(/...\[[0-9]\]\[[0-9]\]\[...\[[0-9]\]\[[0-9]?[0-9]?[0-9]\]\]=Math\[...\[[0-9]\]\[[0-9]?[0-9]?[0-9]\]\]\(Math\[...\[[0-9]\]\[[0-9]?[0-9]?[0-9]\]\]\(1,...\[[0-9]\]\[[0-9]\]\[...\[[0-9]\]\[[0-9]?[0-9]?[0-9]\]\]\),9\);/, '');
  302.  
  303. //Mode selection menu
  304. newStr = newStr.replace(modeStuff[0], `${modeStuff[0]};window.bonkModesObject=${modesObject};window.bonkSetMode=m=>{${modeStuff[1]}${modeVar}=m;${modeStuff[3]}};window.createModeDropdown();`);
  305. //Add mode mutton to map suggestion message
  306. newStr = newStr.replace('v2k[79][d9G[3][624]]([d9G[73]]);', 'v2k[79][d9G[3][624]]([d9G[73]]);' + SUGGESTION_MODE_BUTTON);
  307. //Append mode button
  308. newStr = newStr.replace('d9G[8][d9G[3][455]](d9G[73]);', 'd9G[8][d9G[3][455]](d9G[73]);'+APPEND_SUGGESTION_MODE_BUTTON);
  309.  
  310. //Balance all
  311. newStr = newStr.replace('I8H[7][I8H[1][645]]()', 'I8H[7][I8H[1][645]]() || I8H[7] === "*" || I8H[5][0] == "/balanceall"');
  312. newStr = newStr.replace('I8H[5][0] == G9b.z43(1869)', 'I8H[5][0] == G9b.z43(1869) || I8H[5][0] == "/balanceall"');
  313. newStr = newStr.replace('I8H[67]=I8H[17];break;', BALANCE_SELECTION);
  314. newStr = newStr.replace('parseInt(I8H[5][2]);', 'parseInt(I8H[5][I8H[5][0] == "/balanceall" ? 1 : 2]);');
  315. newStr = newStr.replace('if(I8H[32] == 0)', BALANCE_ALL_MESSAGE);
  316.  
  317. newStr = newStr.replace('if(I8H[5][0] == G9b.z43(1869)', CUSTOM_COMMANDS+'else if(I8H[5][0] == G9b.z43(1869)');
  318. newStr = newStr.replace('u6H[29][I8H[1][644]](G9b.A43(1897),G9b.z43(1871),false);', 'u6H[29][I8H[1][644]](G9b.A43(1897),G9b.z43(1871),false);u6H[29].showStatusMessage("/hhelp - commands from host extension","#cc3333",false);');
  319.  
  320. //Let people join mid game
  321. newStr = newStr.replace('if(u6H[64]){u6H[30]', 'if(window.bonkHost.freejoin||u6H[64]){u6H[30]');
  322.  
  323. /////////////
  324. //Host menu//
  325. /////////////
  326.  
  327. //Add players
  328. newStr = newStr.replace('{Q1h();}};', '{Q1h();}};' + 'window.bonkHost.playerManagement.addPlayer(X9G[1], w3G[0][1][X9G[0][0]]);');
  329.  
  330. //Remove players
  331. newStr = newStr.replace('if(w3G[32][h9G[5]]){', 'if(w3G[32][h9G[5]]){' + 'window.bonkHost.playerManagement.removePlayer(w3G[32][h9G[5]]);');
  332. newStr = newStr.replace('if(w3G[32][c1G[2]]){', 'if(w3G[32][c1G[2]]){' + 'window.bonkHost.playerManagement.removePlayer(w3G[32][c1G[2]]);');
  333.  
  334. //Player click menu
  335. newStr = newStr.replace('if(X9G[1][m9G[2][1379]]){', PLAYER_CLICK_MENU + 'if(X9G[1][m9G[2][1379]]){')
  336.  
  337. //Player click menu position and highligh removal
  338. newStr = newStr.replace('if(m9G[4] == false){', 'if(playerEntry){w3G[26].playerElement=playerEntry;m9G[6].style.removeProperty("left");m9G[6].style.right=0;m9G[6].style.top=([...playerEntry.parentNode.children].indexOf(playerEntry)+1)*43+"px";}' + 'if(m9G[4] == false){');
  339.  
  340. //Highlight
  341. newStr = newStr.replace('X9G[1][m9G[2][457]][m9G[2][456]](G9b.z43(3256));', 'let playerEntry = window.bonkHost.playerManagement.getPlayer(m9G[0][0].target, true); if(playerEntry) {playerEntry.classList.add("newbonklobby_playerentry_menuhighlighted");} else{' + 'X9G[1][m9G[2][457]][m9G[2][456]](G9b.z43(3256));' + '}');
  342.  
  343. //Some bonk function handlers
  344. newStr = newStr.replace('R2k[0][1]});};}', 'R2k[0][1]});};' + 'window.bonkHost.bonkHandlers = this;' + '}');
  345.  
  346. //Some menu functions
  347. newStr = newStr.replace('w3G[60]);}};', 'w3G[60]);}};'+ 'window.bonkHost.menuFunctions = this;');
  348.  
  349. //Player counter
  350. newStr = newStr.replace('if(u6H[5]', 'window.bonkHost.playerCount=s6H[3];if(u6H[5]');
  351.  
  352. //Change team handlers
  353. newStr = newStr.replace('w8G[9]=v8yy;', 'w8G[9]=v8yy;' + "window.bonkHost.playerManagement.movePlayer(w8G[0][0], window.bonkHost.playerCount, w8G[0][1]);");
  354. newStr = newStr.replace('S8G[8]=v8yy;', 'S8G[8]=v8yy;' + "window.bonkHost.playerManagement.movePlayer(this.getLSID(), window.bonkHost.playerCount, S8G[0][0]);");
  355.  
  356. //Get start game function
  357. newStr = newStr.replace('function P5S', 'window.bonkHost.startGameFunction=P5S;document.getElementById("hostPlayerMenuRestartButton").addEventListener("click", window.bonkHost.startGame);' + 'function P5S')
  358.  
  359. //Scores
  360. newStr = newStr.replace(';if(k7k[0][5][k7k[4][114]])', ';' + KEEP_SCORES + 'if(k7k[0][5][k7k[4][114]])');
  361. newStr = newStr.replace('o9k[1][o9k[7][122]]=[0,0,0,0];', 'o9k[1][o9k[7][122]]=' + '(window.bonkHost.scores.length > 0 && document.getElementById("hostPlayerMenuKeepScores").checked) ? window.bonkHost.scores' + ':[0,0,0,0];')
  362.  
  363. //Score counter
  364. newStr = newStr.replace('G9b.A2k(1000,b2k[6]);', 'G9b.A2k(1000,b2k[6]);window.bonkHost.scores=i2k[7][i2k[1]].scores;');
  365.  
  366. //Update menu while in game if host
  367. newStr = newStr.replace('!this[h9G[9][481]]', '!this[h9G[9][481]] && w3G[3].getLSID() != w3G[3].hostID');
  368. newStr = newStr.replace('!this[V9G[1][481]]', '!this[V9G[1][481]] && w3G[3].getLSID() != w3G[3].hostID');
  369.  
  370. //Show menu when in game if host
  371. newStr = newStr.replace('if(u6H[29]){u6H[29][w8H[9][722]]();}', 'if(u6H[29]){u6H[29][w8H[9][722]]();' + `if(u6H[11].hostID == u6H[11].getLSID())window.bonkHost.playerManagement.show();` + '}');
  372.  
  373. //Hide menu when not in game
  374. newStr = newStr.replace('w1G[5]=v8yy;', 'w1G[5]=v8yy;' + 'window.bonkHost.playerManagement.hide();');
  375. newStr = newStr.replace('c1G[5]=v8yy;', 'c1G[5]=v8yy;' + 'window.bonkHost.playerManagement.hide();');
  376.  
  377. //Add default click handler (mainly for closing the dropdown menu)
  378. newStr = newStr.replace('=e6h;', '=e6h;' + 'document.getElementById("hostPlayerMenu").onclick=e6h;');
  379.  
  380. //Handle host change
  381. newStr = newStr.replace('l5h(G9b.z43(3279),G9b.A43(3276),false);}', 'window.bonkHost.playerManagement.show();' + 'l5h(G9b.z43(3279),G9b.A43(3276),false);}' + 'else{window.bonkHost.playerManagement.hide();}');
  382. newStr = newStr.replace('C0z(G9b.A43(2039));', 'C0z(G9b.A43(2039));' + 'window.bonkHost.playerManagement.show();');
  383.  
  384. //Teamlock
  385. newStr = newStr.replace('if(w3G[0][2][V9G[1][662]])', 'document.getElementById("hostPlayerMenuTeamlock").checked=w3G[0][2].tl;' + 'if(w3G[0][2][V9G[1][662]])');
  386.  
  387. //Handle an error when clicking host menu. MIGHT BREAK IN RARE CASES WITH OTHER MODS because it's missing {}, but it should be fine
  388. newStr = newStr.replace('t9G[5]=v8yy;', 't9G[5]=v8yy;' + 'if(w3G[26][t9G[5][1372]]!=null)'); console.log("Bonk Host injector run");
  389. return newStr;
  390. }
  391.  
  392. if(!window.bonkCodeInjectors) window.bonkCodeInjectors = [];
  393. window.bonkCodeInjectors.push(bonkCode => {
  394. try {
  395. return injector(bonkCode);
  396. } catch (error) {
  397. alert(
  398. `Whoops! Bonk Host was unable to load.
  399. This may be due to an update to Bonk.io. If so, please report this error!
  400. This could also be because you have an extension that is incompatible with \
  401. Bonk Host, such as the Bonk Leagues Client. You would have to disable it to use \
  402. Bonk Host.`);
  403. throw error;
  404. }
  405. });
  406.  
  407. console.log("Bonk Host injector loaded");