Code Injector - Bonk.io

Allows different userscripts to define functions that modify the game's code

  1. // ==UserScript==
  2. // @name Code Injector - Bonk.io
  3. // @version 1.0.4
  4. // @description Allows different userscripts to define functions that modify the game's code
  5. // @author Excigma & kklkkj
  6. // @namespace https://greasyfork.org/users/416480
  7. // @license GPL-3.0
  8. // @match https://bonk.io/gameframe-release.html
  9. // @run-at document-body
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. // What this does:
  14. // - Finds other userscripts that have defined their injectors in `window.bonkCodeInjectors`, and runs them
  15.  
  16. // Much of the code below was copied from or inspired by https://github.com/kklkkj/kklee/blob/master/src/runInjectors.js
  17. // Credits to https://github.com/kklkkj for creating this system where multiple
  18. // extensions can edit different parts of Bonk.io' code.
  19.  
  20. // This script was originally made to inject kklkkj's kklee as a userscript
  21.  
  22. // Go thank kklkkj for this
  23.  
  24. (function () {
  25. // Original `document.head.appendChild` function that we're going to overwrite
  26. const _appendChild = document.head.appendChild;
  27. const log = (msg) => console.log(`%c[Injector] ${msg}`, "color: #06c26d");
  28.  
  29. // When RequireJS tries to add alpha2s.js to <head> we will intercept it,
  30. // and patch it using functions defined by other scripts in window.bonkCodeInjectors
  31. document.head.appendChild = function (...args) {
  32. // `args?.[0]?.src.includes("alpha2s.js")` is more difficult to read
  33. if (args[0] && args[0]?.tagName === "SCRIPT" && args[0]?.src?.includes("alpha2s.js")) {
  34. // Store the url of the original unmodified alpha2s.js
  35. // Add ? to prevent double injection
  36. const code_url = args[0].src + "?";
  37.  
  38. // Remove the src attribute so it doesn't try to load the original alpha2s.js script
  39. args[0].removeAttribute("src");
  40.  
  41. (async function () {
  42. // Fetch alpha2s.js
  43. log("Fetching alpha2s.js...");
  44. const request = await fetch(code_url).catch(error => console.error(error));
  45.  
  46. // Error fetching alpha2s.js (for example http 404)
  47. if (!request.ok) {
  48. log("Failed to fetch alpha2s.js");
  49. alert("An error occurred whilst fetching game code");
  50.  
  51. // Fallback to what bonk.io would normally do
  52. args[0].src = code_url;
  53. return _appendChild.apply(document.head, args);
  54. }
  55.  
  56. let code = await request.text();
  57.  
  58. log("Patching alpha2s.js...");
  59. const start_time = performance.now();
  60.  
  61. // No bonkCodeInjectors found, this might mean that the user does not have any bonk userscripts installed
  62. // or that they failed to load before this script
  63.  
  64. // I removed the alert because people who prefer userscripts over extensions likely
  65. // enjoy the flexibility of being able to disable userscripts easily
  66. // and it's possible that they will have all their userscripts disabled at one time
  67.  
  68. if (!window.bonkCodeInjectors) {
  69. // Still log to the console
  70. log(
  71. "Did not find any Bonk.io userscripts to load. This may be an error, make sure you have scripts installed."
  72. );
  73. } else {
  74. // Loop through `bonkCodeInjectors` and pass alpha2s.js' code in for them to modify
  75. let error_notified = false;
  76. for (const injector of window.bonkCodeInjectors) {
  77. try {
  78. // Run injector from other userscripts
  79. if (typeof injector === "function") code = injector(code);
  80. else {
  81. log("Injector was not a function");
  82. console.log(injector);
  83. }
  84. // I could check if code === injector(code); and throw an error if the code
  85. // did not change, but Salama also does in their userscripts which would caue
  86. // a double up in alert()s, which is not very nice
  87. } catch (error) {
  88. // Only notify the user once if any userscript fails to load
  89. // helpful to prevent spamming alerts()
  90. if (!error_notified) {
  91. // An injector from one of the other userscripts failed to load
  92. alert("One of your Bonk.io userscripts was unable to be loaded");
  93. error_notified = true;
  94. }
  95.  
  96. console.error(error);
  97. }
  98. }
  99. }
  100.  
  101. const end_time = performance.now();
  102. log(`Patched alpha2s.js successfully (${(end_time - start_time).toFixed(0)}ms)`);
  103.  
  104. // Add the new script to the <script>'s contents
  105. args[0].textContent = code;
  106.  
  107. // Make RequireJS think that the script loaded
  108. args[0].dispatchEvent(new Event("load"));
  109.  
  110. // Append the modified <script> tag to document.head
  111. return _appendChild.apply(document.head, args);
  112. })().catch(error => {
  113. // Error patching alpha2s.js somewhere
  114. log("Failed to inject code into alpha2s.js");
  115. console.error(error);
  116. alert("An error occurred whilst patching game code");
  117.  
  118. // Fallback to what bonk.io would normally do without this userscript
  119. args[0].src = code_url;
  120. return _appendChild.apply(document.head, args);
  121. });
  122. } else {
  123. return _appendChild.apply(document.head, args);
  124. }
  125. };
  126. })();