您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download And Watch Replays
// ==UserScript== // @name Diep.io Replay Beta // @author A Happy peepo // @namespace peepo // @version 0.3.0 // @description Download And Watch Replays // @match *://diep.io/ // @run-at document-start // @license Apache License 2.0 // @require https://cdn.jsdelivr.net/gh/Qwokka/WAIL@41c655434da60f14dfda88813b0cef9000c79ca6/wail.js // @grant none // ==/UserScript== "use strict"; WebAssembly.instantiateStreaming = (r, i) => r.arrayBuffer().then(b => WebAssembly.instantiate(b, i)); class PacketHook extends EventTarget { static get CONST() { return { BUILD: "6ac60ba9d55769e868510c25366547d916b023b3", RECV_PACKET_INDEX: 471, MALLOC: "sa", FREE: "X", } } constructor(hook) { super(); this.HEAPU8 = new Uint8Array(0); this.HEAP32 = new Int32Array(0); this.wasm = null; this._inject(hook); this._hijack(); } _modify(bin, imports) { console.log('Modifying WASM'); const wail = this.wail = new WailParser(new Uint8Array(bin)); const recvPacket = this.recvPacket = wail.getFunctionIndex(PacketHook.CONST.RECV_PACKET_INDEX); const mainHook = wail.addImportEntry({ moduleStr: "hook", fieldStr: "mainHook", kind: "func", type: wail.addTypeEntry({ form: "func", params: ["i32", "i32"], returnType: "i32" }) }); wail.addExportEntry(recvPacket, { fieldStr: "recvPacket", kind: "func", }); wail.addCodeElementParser(null, function({ index, bytes }) { if (index === recvPacket.i32()) { return new Uint8Array([ OP_GET_LOCAL, 0, OP_GET_LOCAL, 1, OP_CALL, ...VarUint32ToArray(mainHook.i32()), OP_IF, VALUE_TYPE_BLOCK, OP_RETURN, OP_END, ...bytes ]); } return false; }); wail.parse(); return wail.write(); } _inject(mainHook) { const _initWasm = WebAssembly.instantiate; WebAssembly.instantiate = (bin, imports) => { this.imports = {}; this.imports = Object.assign(this.imports, imports); bin = this._modify(bin, imports); imports.hook = { mainHook }; return _initWasm(bin, imports).then((wasm) => { this.wasm = wasm.instance; const memory = Object.values(this.wasm.exports).find(e => e instanceof WebAssembly.Memory); this.HEAPU8 = new Uint8Array(memory.buffer); this.HEAP32 = new Int32Array(memory.buffer); this.malloc = this.wasm.exports[PacketHook.CONST.MALLOC]; this.free = this.wasm.exports[PacketHook.CONST.FREE]; console.log('Module exports done!\n\t- Hook.free\n\t- Hook.malloc\n\t- Hook.send\n\t- Hook.recv\n\t- Hook.addEventListener(\'clientbound\', ({data}) => console.log(data));\n\t- Hook.addEventListener(\'serverbound\', ({data}) => console.log(data));'); return wasm }).catch(err => { console.error('Error in loading up wasm:'); throw err; }) }; } _hijack() { const that = this; window.Object.defineProperty(Object.prototype, "postRun", { get() {}, set(postRun) { delete Object.prototype.postRun this.postRun = postRun; that.Module = this; console.log('Module exports done! Hook.Module'); }, configurable: true, }); } recv(buf) { const { malloc, free, HEAP32, HEAPU8 } = this; buf = new Uint8Array(buf); const ptr = malloc(buf.byteLength); HEAPU8.set(buf, ptr); this.wasm.exports.recvPacket(ptr, buf.byteLength) free(ptr); } } function ab2str(buf) { return String.fromCharCode.apply(null, buf); } function str2ab(str) { var buf = new ArrayBuffer(str.length); var bufView = new Uint8Array(buf); for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return new Uint8Array(buf); } Array.prototype.insert = function(index, item) { this.splice(index, 0, item); }; var buffer = []; var replaylengths = []; const Hook = window.Hook = new PacketHook(function(ptr, len) { console.log(ptr,len); if (Hook.HEAPU8[ptr] === 7 && !playback) { buffer.insert(0, str2ab(PacketHook.CONST.BUILD)); replaylengths.insert(0, 0); } if (Hook.HEAPU8[ptr] === 2 || Hook.HEAPU8[ptr] === 10 || Hook.HEAPU8[ptr] === 11 || Hook.HEAPU8[ptr] === 13) return 0; if (Hook.HEAPU8[ptr] >= 127) { Hook.HEAPU8[ptr] -= 127; return 0; } if (playback) return 1; if (recording) { let temp1 = new Uint32Array([len]); let temp2 = new Float32Array([mouseX]); let temp3 = new Float32Array([mouseY]); let temp4 = Hook.HEAPU8.slice(ptr, ptr + len); if (Hook.HEAPU8[ptr] === 0) replaylengths[0]++; buffer[0] = concatenate(buffer[0], new Uint8Array(temp1.buffer), new Uint8Array(temp2.buffer), new Uint8Array(temp3.buffer), temp4) } }); var originmouseX = window.innerWidth / 2; var originmouseY = window.innerHeight / 2; let referenceWidth = window.innerWidth / 1920; let referenceHeight = window.innerHeight / 1080; var gamescale = referenceWidth < referenceHeight ? referenceHeight : referenceWidth; function rezize() { originmouseX = window.innerWidth / 2; originmouseY = window.innerHeight / 2; let referenceWidth = window.innerWidth / 1920; let referenceHeight = window.innerHeight / 1080; gamescale = referenceWidth < referenceHeight ? referenceHeight : referenceWidth; } addEventListener('resize', rezize); var mouseX = originmouseX; var mouseY = originmouseY; function getcurrentpos(p) { mouseX = p.pageX / window.innerWidth / gamescale; mouseY = p.pageY / window.innerHeight / gamescale; } addEventListener('mousemove', getcurrentpos, false); var recording = true; var playback = false; var btn = document.createElement("button"); btn.innerHTML = "Download Replay"; btn.style.zIndex = 1; btn.style.position = "absolute"; var selection = null; btn.onclick = function() { if (buffer.length === 1) { downloadbuffer(buffer[0]); } else if (selection === null) { var myParent = document.body; //Create array of options to be added var array = buffer.map((x, index) => "index:" + index + " length:" + (replaylengths[index] * 0.04) + " seconds") //Create and append select list var selectList = []; var y = 50; //Create and append the options for (var i = 0; i < array.length; i++) { var option = document.createElement("button"); option.value = i; option.innerHTML = array[i]; option.style.zIndex = 1; option.style.position = "absolute"; option.style.top = y + "px"; option.onclick = function() { downloadbuffer(buffer[this.value]); } myParent.appendChild(option); y += option.offsetHeight; option.style.top = y + "px"; selectList.push(option); } selection = selectList; return; } else if (selection !== null) { selection.forEach(x => x.remove()) selection = null; } }; var downloadbuffer = function(buffer) { if (recording) { const blob = new Blob([buffer], { type: 'application/octet-stream' }) const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = "diep.io replay.bin" document.body.appendChild(a) a.style.display = 'none' a.click() a.remove() setTimeout(() => window.URL.revokeObjectURL(url), 1000) //btn.innerHTML = "Start Recording"; //buffer = concatenate(new Uint8Array([1, 0, 0, 0]), new Uint8Array([7])); } // else { //btn.innerHTML = "Stop Recording"; //} //recording = !recording } document.body.appendChild(btn); var input = document.createElement("input"); input.id = "file-input"; input.type = "file"; input.innerHTML = "Load Recording"; input.style.zIndex = 1; input.style.left = "120px"; input.style.position = "absolute"; document.body.appendChild(input); document.getElementById('file-input') .addEventListener('change', readSingleFile, false); function readSingleFile(e) { var file = e.target.files[0]; if (!file) { return; } var reader = new FileReader(); reader.onload = function(e) { var contents = e.target.result; var temp2 = new Uint8Array(contents); playback = true; var replay_id = ab2str(temp2.slice(0, 40)); var build_id = PacketHook.CONST.BUILD + '-' + replay_id; var index = 40; console.log("packet_id", replay_id); WebSocket.prototype.send = function(data) { if (playback) { this.onclose = undefined; this.onmessage = undefined; this.onerror = undefined; WebSocket.prototype.send = function() {}; return; } }; Object.defineProperty(WebSocket.prototype, 'readyState', { get: function() { return 1 } }); var onload = function() { window.input.set_convar("net_predict_movement", false); document.getElementById('canvas').onmousemove = undefined; const myInterval = setInterval(function() { let length = (new Uint32Array(temp2.slice(index, index + 4).buffer))[0] index += 4; let mouseX = (new Float32Array(temp2.slice(index, index + 4).buffer))[0] index += 4; let mouseY = (new Float32Array(temp2.slice(index, index + 4).buffer))[0] index += 4; window['input']['mouse'](mouseX * window.innerWidth * gamescale, mouseY * window.innerHeight * gamescale); if (temp2[index] === 2 || temp2[index] > 10) { index += length; return; } temp2[index] += 127; Hook.recv(temp2.slice(index, index + length)); index += length; if (index >= temp2.length) { clearInterval(myInterval); window.input.set_convar("net_predict_movement", true); location.reload(); } }, 40) } //here was wasm modifier, needs some work onload(); }; reader.readAsArrayBuffer(file); } function concatenate(...arrays) { // Calculate byteSize from all arrays let size = arrays.reduce((a, b) => a + b.byteLength, 0) // Allcolate a new buffer let result = new Uint8Array(size) // Build the new array let offset = 0 for (let arr of arrays) { result.set(arr, offset) offset += arr.byteLength } return result }