Io Record

This script is in beta testing !! Record any io game (agma.io only for now)

  1. // ==UserScript==
  2. // @name Io Record
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.10
  5. // @author Big watermelon
  6. // @description This script is in beta testing !! Record any io game (agma.io only for now)
  7. // @match *://agma.io/*
  8. // @license All Rights Reserved
  9. // @icon data:image/png;base64,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
  10. // @grant unsafeWindow
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @run-at document-start
  14. // ==/UserScript==
  15.  
  16. /*
  17.  
  18. Copyright © 2024 Big watermelon. All Rights Reserved.
  19. This work is proprietary and may not be copied, distributed, or modified without explicit permission.
  20.  
  21. */
  22.  
  23. // FIXIT: on death doesnt show death screen cuz packet 12 is useless and sora is dumb
  24. // FIXIT: make changelog fully text dependable otherwise people scared I'll hack them via XSS
  25. // FIXIT: why shit drop works 1/4 times (on my OS)
  26. // FIXIT: allow moving if spec mode while watching record
  27. // TODO: next frame -> to keydown to allow fastforward
  28. /* TODO: maybe some shits to show
  29. Interval: 29794 ms
  30. Packets: 965
  31. Bytes: 370 Ko
  32.  
  33. console.log(`Interval: ${Math.round(record[record.length - 1].timeStamp - record[0].timeStamp)}ms\nPackets: ${record.length\nBytes: ${Math.round(record.map(x=>x.data.byteLength).reduce((a, b)=>a+b)/1000)}Ko`);`
  34.  
  35. */
  36. // TODO: visual indicator when viewing record ?
  37. /* TODO: optimize files deflate
  38.  
  39. pako.deflate(new Uint8Array(clip.map(({offset, data}) => new Uint8Array([
  40. ...new Uint8Array(new Uint32Array([offset]).buffer),
  41. ...new Uint8Array(data)
  42. ])).reduce((a, b) => [...a, ...b], [])))
  43.  
  44. */
  45. // TODO: make files into binaries
  46. // FIXIT: depending server some clips may do shit ?
  47. /* FIXIT: I know why cells disapear and it does weird shit
  48. some packets are missing like 10, 11, 12, 32
  49. so game doesnt really understand the state its in
  50. */
  51. // FIXIT: change extension name and clip to binaries [Uint32 "offset", ... "packet"]
  52. // TODO: allow multi file drop -> means add keybind for next/prev clip
  53.  
  54. (function() {
  55. 'use strict';
  56.  
  57. if (unsafeWindow.top !== unsafeWindow.self || document.querySelector('title')?.textContent?.includes('Just a moment'))
  58. return;
  59.  
  60. const settings = Object.assign({
  61. saveRecordKey: 'o',
  62. pauseKey: ' ',
  63. nextFrameKey: 'ArrowRight',
  64. escapeViewKey: 'Escape',
  65. recordAnimations: false,
  66. recordLeaderboard: false,
  67. recordMovingBorders: true,
  68. recordFor: 10000,
  69. fetchChangeLog: true
  70. }, GM_getValue('settings', {}));
  71.  
  72. const SCRIPT_VERSION = GM_info?.script?.version;
  73. let clientVersion = 'Unknown';
  74.  
  75. function versionAlert(clipVersion, errorDetail) {
  76. swal({
  77. title: 'Io Record',
  78. text: `<span style="color:red;font-weight:bold;">An error occured while loading this clip.</span>
  79. <br>
  80. <span>Clip Version: ${clipVersion}</span>
  81. <br>
  82. <span>Io Record Version: ${SCRIPT_VERSION},${clientVersion}</span>
  83. <br>
  84. <span>If your the script isn't up to date you may not be able to view some clips.</span>
  85. <br>
  86. <span style="font-size:12pt;">${errorDetail}</span>`,
  87. html: true,
  88. type: 'error'
  89. });
  90. }
  91. function versionDiff(a, b) { // a < b
  92. a = a.split('.');
  93. b = b.split('.');
  94. while (a.length && b.length) {
  95. if (Number(a.shift()) < Number(b.shift()))
  96. return true;
  97. }
  98. return false;
  99. }
  100. function serializeArray(frames) {
  101. return SCRIPT_VERSION + ',' + clientVersion + '\n' + frames.map(frame => Math.round(frame.timeStamp - frames[0].timeStamp) + ': ' + btoa(String.fromCharCode(...new Uint8Array(frame.data)))).join('\n');
  102. }
  103. function deserializeString(frames) {
  104. const [framesep, offsetsep] = frames.includes('\n') ? ['\n', ': '] : ['|', ':'];
  105. frames = frames.split(framesep);
  106. const [s, c] = (frames[0].includes(offsetsep) ? '0.0.8,ag255' : frames.shift()).split(',');
  107. try {
  108. return frames.map(frame => {
  109. const [offset, base64Data] = frame.split(offsetsep);
  110. const binaryString = atob(base64Data);
  111. const buffer = new Uint8Array(binaryString.length);
  112. for (let i = 0; i < binaryString.length; i++)
  113. buffer[i] = binaryString.charCodeAt(i);
  114. return { id: buffer[0], offset: Number(offset), data: buffer.buffer };
  115. });
  116. } catch (error) {
  117. if (versionDiff(s, SCRIPT_VERSION) || c < clientVersion)
  118. versionAlert(s + ',' + c, error.name + ': ' + error.message);
  119. throw error;
  120. }
  121. }
  122.  
  123. const MANDATORY_PACKETS = [10, 11, 12, 20, 32, 33, 48, 49, 50, 65, 66],
  124. CLEAR_ALL = { data: new Uint8Array([20]).buffer },
  125. FAKE_CELL_UPDATE = { data: new Uint8Array([10, 0, 0, 0, 0, 0, 0]).buffer };
  126.  
  127. const record = [];
  128. let isPaused = true,
  129. viewedRecord = null,
  130. wsOnmessage,
  131. frameIndex = 0;
  132.  
  133. unsafeWindow.addEventListener('keydown', event => viewedRecord && Object.values(settings).includes(event.key) && event.stopImmediatePropagation());
  134. unsafeWindow.addEventListener('keyup', event => {
  135. if ($('input, textarea').is(':focus')) return;
  136. if (viewedRecord) {
  137. if (event.key == settings.nextFrameKey) {
  138. if (frameIndex >= viewedRecord.length) return;
  139. isPaused = true;
  140. let current;
  141. do {
  142. wsOnmessage(current = viewedRecord[frameIndex++]);
  143. } while (frameIndex <= viewedRecord.length && current.id != 10);
  144. } else if (event.key == settings.pauseKey) {
  145. if (isPaused) {
  146. isPaused = false;
  147. goto();
  148. } else {
  149. isPaused = true;
  150. }
  151. } else if (event.key == settings.escapeViewKey) {
  152. frameIndex = 0;
  153. wsOnmessage(CLEAR_ALL);
  154. viewedRecord = null;
  155. }
  156. } else if (event.key == settings.saveRecordKey) {
  157. const link = document.createElement("a");
  158. link.href = URL.createObjectURL(new Blob([serializeArray(record)], { type: 'text/plain' }));
  159. const d = new Date();
  160. link.download = `agma-clip.bin`;
  161. document.body.appendChild(link);
  162. link.click();
  163. document.body.removeChild(link);
  164. } else {
  165. return;
  166. }
  167. event.stopImmediatePropagation();
  168. event.preventDefault();
  169. });
  170.  
  171. function recordPacket(message) {
  172. const { timeStamp, data } = message;
  173. const id = new DataView(data).getUint8(0);
  174. if (
  175. id == 11 && data.byteLength == 3
  176. || id == 12 && data.byteLength == 5
  177. )
  178. return;
  179. if (viewedRecord) {
  180. if (id == 10) return isPaused && wsOnmessage(FAKE_CELL_UPDATE);
  181. else if (id == 11 || id == 12 || id == 17) return; // prevents spectator movement
  182. }
  183. wsOnmessage(message);
  184. while (timeStamp - record[0]?.timeStamp > settings.recordFor)
  185. record.shift();
  186. if (
  187. viewedRecord
  188. || !settings.recordAnimations && id == 33
  189. || !settings.recordLeaderboard && [48, 49, 50].includes(id)
  190. || !settings.recordMovingBorders && [65, 66].includes(id)
  191. || !MANDATORY_PACKETS.includes(id)
  192. )
  193. return;
  194. record.push({ timeStamp, data });
  195. }
  196.  
  197. function goto() {
  198. if (!viewedRecord) return;
  199. if (!frameIndex)
  200. wsOnmessage(CLEAR_ALL);
  201. const current = viewedRecord[frameIndex++];
  202. MANDATORY_PACKETS.includes(current.id) && wsOnmessage(current);
  203. if (isPaused)
  204. return;
  205. if (viewedRecord[frameIndex])
  206. setTimeout(goto, viewedRecord[frameIndex].offset - current.offset);
  207. else setTimeout(() => {
  208. frameIndex = 0;
  209. wsOnmessage(CLEAR_ALL);
  210. isPaused = true;
  211. }, 10);
  212. }
  213.  
  214. const originalDefineProperty = unsafeWindow.Object.defineProperty;
  215. unsafeWindow.Object.defineProperty = function(obj, prop, descriptor) {
  216. if (obj instanceof WebSocket && obj.url.includes('.agma.io')) {
  217. obj.addEventListener('message', recordPacket);
  218. originalDefineProperty(obj, 'onmessage', {
  219. set: function(onmessage) { wsOnmessage = onmessage; },
  220. get: function() { return wsOnmessage; }
  221. });
  222. }
  223. return originalDefineProperty(obj, prop, descriptor);
  224. }
  225.  
  226. /* Temporarily disabled
  227.  
  228. const originalSend = unsafeWindow.WebSocket.prototype.send;
  229. unsafeWindow.WebSocket.prototype.send = function() {
  230. // this just prevents mouse position
  231. if (!arguments[0]?.getUint8(0) && viewedRecord != null) return;
  232. return originalSend.apply(this, arguments);
  233. }
  234.  
  235. */
  236.  
  237. Object.defineProperties(HTMLBodyElement.prototype, { // risky but mandatory
  238. ondragstart: { get: () => null, set: () => null, configurable: true },
  239. ondrop: { get: () => null, set: () => null, configurable: true },
  240. ondragenter: { get: () => null, set: () => null, configurable: true },
  241. ondragover: { get: () => null, set: () => null, configurable: true }
  242. });
  243.  
  244. let loaded = false;
  245. unsafeWindow.addEventListener('load', () => {
  246. if (loaded || typeof swal == 'undefined') return;
  247. loaded = true;
  248. for (const { src } of document.scripts) {
  249. if (clientVersion = src.match("ag[0-9]+")?.[0])
  250. break;
  251. }
  252. if (settings.fetchChangeLog) {
  253. try {
  254. const xhr = new XMLHttpRequest();
  255. xhr.open('GET', 'https://api.github.com/repos/Grosse-pasteque/io-record/contents/CHANGELOG', true);
  256. xhr.setRequestHeader('Accept', 'application/json');
  257. xhr.setRequestHeader('Authorization', 'Bearer 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');
  258. xhr.setRequestHeader('X-GitHub-Api-Version', '2022-11-28');
  259. xhr.onreadystatechange = function () {
  260. if (xhr.readyState === XMLHttpRequest.DONE) {
  261. if (xhr.status === 200) {
  262. const changelog = atob(JSON.parse(xhr.responseText).content);
  263. if (changelog[0] != GM_getValue('changelog', '0')) {
  264. GM_setValue('changelog', changelog[0])
  265. swal({ title: '', text: changelog.slice(1), html: true })
  266. }
  267. } else {
  268. console.error('Error:', xhr.status, xhr.statusText);
  269. }
  270. }
  271. };
  272. xhr.send();
  273. } catch (e) {
  274. console.error("IO-Record, couldn't fetch CHANGELOG:", e);
  275. }
  276. }
  277. const canvas = document.getElementById('canvas');
  278. canvas.addEventListener("dragenter", event => {
  279. event.preventDefault();
  280. canvas.style.border = '5px solid green';
  281. });
  282. canvas.addEventListener("dragover", event => event.preventDefault());
  283. canvas.addEventListener("dragleave", event => {
  284. if (event.target != canvas) return;
  285. event.preventDefault();
  286. canvas.style.border = '';
  287. });
  288. canvas.addEventListener('drop', event => {
  289. canvas.style.border = '';
  290. const file = event.dataTransfer.files[0];
  291. if (!file) return;
  292. const reader = new FileReader();
  293. reader.onload = e => {
  294. frameIndex = 0;
  295. isPaused = false;
  296. viewedRecord = deserializeString(e.target.result);
  297. goto();
  298. };
  299. reader.readAsText(file);
  300. event.preventDefault();
  301. });
  302. const settingPageId = Math.random() * 10e17;
  303.  
  304. const settingTab = document.createElement('button');
  305. settingTab.id = 'settingTab' + settingPageId;
  306. settingTab.className = 'setting-tablink';
  307. settingTab.onclick = openSettingPage.bind(null, settingPageId);
  308. settingTab.innerText = 'Io Record';
  309.  
  310. const settingPage = document.createElement('div');
  311. settingPage.id = 'settingPage' + settingPageId;
  312. settingPage.className = 'setting-tabcontent';
  313. settingPage.innerHTML = `
  314. <div class="col-md-10 col-md-offset-1 stng" style="padding-left:20px;padding-right:10px;max-height:550px;overflow:hidden auto;margin:0;width:calc(100% - 5px);">
  315. <span class="hotkey-paragraph"> Keybinds</span>
  316. <div class="row stng-row">
  317. Save Record
  318. <div class="hotkey-input" data-name="saveRecordKey">${settings.saveRecordKey}</div>
  319. <br>
  320. Pause View
  321. <div class="hotkey-input" data-name="pauseKey">${settings.pauseKey}</div>
  322. <br>
  323. Next Frame
  324. <div class="hotkey-input" data-name="nextFrameKey">${settings.nextFrameKey}</div>
  325. <br>
  326. Escape Viewed Record
  327. <div class="hotkey-input" data-name="escapeViewKey">${settings.escapeViewKey}</div>
  328. </div>
  329. <span class="hotkey-paragraph"> Record</span>
  330. <div class="row stng-row">
  331. Animations
  332. <input type="checkbox" data-name="recordAnimations" ${settings.recordAnimations ? "checked" : ''}>
  333. <br>
  334. Leaderboard
  335. <input type="checkbox" data-name="recordLeaderboard" ${settings.recordLeaderboard ? "checked" : ''}>
  336. <br>
  337. Moving borders
  338. <input type="checkbox" data-name="recordMovingBorders" ${settings.recordMovingBorders ? "checked" : ''}>
  339. </div>
  340. <span class="hotkey-paragraph"> Other</span>
  341. <div class="row stng-row">
  342. Records Length (seconds)
  343. <input type="number" min="0" class="hotkey-input" style="outline:none;border:none;" value="${~~(settings.recordFor / 1000)}">
  344. <br>
  345. Fetch Change Log
  346. <input type="checkbox" data-name="fetchChangeLog" ${settings.fetchChangeLog ? "checked" : ''}>
  347. </div>
  348. </div>
  349. `;
  350.  
  351. function onchange() {
  352. settings[this.dataset.name] = this.checked;
  353. }
  354. function onclick() {
  355. this.classList.add('selected');
  356. const handle = event => {
  357. if (event.type === 'keyup')
  358. settings[this.dataset.name] = this.innerText = event.key;
  359. this.classList.remove('selected');
  360. unsafeWindow.removeEventListener('mousedown', handle);
  361. unsafeWindow.removeEventListener('keyup', handle);
  362. event.preventDefault();
  363. event.stopPropagation()
  364. };
  365. unsafeWindow.addEventListener('mousedown', handle);
  366. unsafeWindow.addEventListener('keyup', handle)
  367. }
  368. function oncontextmenu(event) {
  369. this.innerText = '';
  370. settings[this.dataset.name] = null;
  371. event.preventDefault()
  372. }
  373. settingPage.querySelectorAll('input[type=checkbox]').forEach(input => input.onchange = onchange.bind(input));
  374. settingPage.querySelector('input[type=number]').onchange = event => settings.recordFor = 1000 * event.target.value;
  375. settingPage.querySelectorAll('div.hotkey-input').forEach(hotkey => {
  376. hotkey.oncontextmenu = oncontextmenu.bind(hotkey);
  377. hotkey.onclick = onclick.bind(hotkey);
  378. });
  379.  
  380. const style = document.createElement('style');
  381. style.innerHTML = `
  382. #settingPage${settingPageId} input {
  383. position: absolute;
  384. display: inline-block;
  385. right: 0;
  386. }
  387. #settingPage${settingPageId} .hotkey-input {
  388. max-width: 90px;
  389. }
  390. #settingPage${settingPageId} > div::-webkit-scrollbar {
  391. width: 8px;
  392. height: 8px;
  393. }
  394. #settingPage${settingPageId} > div::-webkit-scrollbar-track {
  395. background: #282934;
  396. border-radius: 10px;
  397. }
  398. #settingPage${settingPageId} > div::-webkit-scrollbar-thumb {
  399. background-color: #df8500;
  400. border-radius: 10px;
  401. border: 2px solid #282934;
  402. }
  403. `;
  404.  
  405. const setting = document.getElementById('setting');
  406. setting.firstElementChild.appendChild(settingTab);
  407. setting.appendChild(settingPage);
  408. document.body.appendChild(style);
  409. });
  410. unsafeWindow.onbeforeunload = () => void GM_setValue('settings', settings);
  411. console.log(`🎥 Io Record - ${SCRIPT_VERSION} loaded!`);
  412. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址