Diep.io Replay Beta

Download And Watch Replays

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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
}