您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Pixel Radar for PPF Clones.
// ==UserScript== // @name B7P Pix-Radar // @namespace https://tampermonkey.net/ // @version 1.1 // @description Pixel Radar for PPF Clones. // @author B7P.eu // @grant none // @icon https://port.b7p.eu/static/pfp.png // @connect githubusercontent.com // @connect github.com // @connect canvasland.net/* // @match *://canvasland.net//* // ==/UserScript== let notificationRadius = 300; const NOTIFICATION_TIME = 2000; let pixelList = []; let canvas; let notifCircle; const args = window.location.href.split(','); let globalScale = 1; let viewX = parseInt(args[args.length - 3]); let viewY = parseInt(args[args.length - 2]); const PING_OP = 0xB0; const REG_MCHUNKS_OP = 0xA3; const PIXEL_UPDATE_OP = 0xC1; const REG_CANVAS_OP = 0xA0; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } function init() { setTimeout(radarMain); } function showInfo(info) { if (info.text.length > 0) { closeModal(); const wrapper = document.createElement('div'); wrapper.innerHTML = ` <div class="Alert show" id="my_modal"> <h2>Останній закріп</h2> <p>${info.text}</p> <button type="button" id="my_button">OK</button> </div> `; document.body.appendChild(wrapper); const button = document.querySelector('#my_button'); button.addEventListener('click', closeModal); } } function closeModal() { const modal = document.querySelector('#my_modal'); if (modal) modal.remove(); } async function loadFile(src) { const resp = await fetch(src); const blob = await resp.blob(); return new File([blob], 'result.png', { type: 'image/png', }); } async function loadInfo(src) { const resp = await fetch(src); return await resp.json(); } function worldToScreen(x, y) { return [ ((x - viewX) * globalScale) + (canvas.width / 2), ((y - viewY) * globalScale) + (canvas.height / 2), ]; } function render() { try { const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height) if (globalScale < 0.8) { const curTime = Date.now(); let index = pixelList.length; while (index > 0) { index--; let [setTime, x, y, i, j] = pixelList[index]; const timePassed = curTime - setTime; if (timePassed > NOTIFICATION_TIME) { pixelList.splice(index, 1); continue; } const [sx, sy] = worldToScreen(x, y) .map((z) => z + globalScale / 2); if (sx < 0 || sy < 0 || sx > canvas.width || sx > canvas.height) { pixelList.splice(index, 1); continue; } const notRadius = timePassed / NOTIFICATION_TIME * notificationRadius; const circleScale = notRadius / 100; ctx.save(); ctx.scale(circleScale, circleScale); ctx.drawImage( notifCircle, Math.round(sx / circleScale - 100), Math.round(sy / circleScale - 100), ); ctx.restore(); } } } catch (err) { console.error(`Render error`, err,); } setTimeout(render, 10); } function addPixel(x, y, i, j) { for (let k = 0; k < pixelList.length; k++) { if (pixelList[k][3] === i && pixelList[k][4] === j) { pixelList[k][1] = x; pixelList[k][2] = y; return; } } pixelList.unshift([Date.now(), x, y, i, j]); } function getPixelFromChunkOffset(i, j, offset, canvasSize) { const tileSize = 256; const x = i * tileSize - canvasSize / 2 + offset % tileSize; const y = j * tileSize - canvasSize / 2 + Math.trunc(offset / tileSize); //const x = i * tileSize - canvasSize / 2 + 128; //const y = j * tileSize - canvasSize / 2 + 128; return [x, y]; } function renderPixel(i, j, offset) { const canvasSize = 65536; const [x, y] = getPixelFromChunkOffset(i, j, offset, canvasSize); addPixel(x, y, i, j); } function renderPixels({i, j, pixels}) { pixels.forEach((pxl) => { const [offset, color] = pxl; renderPixel(i, j, offset); }); } function clamp(n, min, max) { return Math.max(min, Math.min(n, max)); } function updateScale(viewscale) { globalScale = viewscale; notificationRadius = clamp(viewscale * 10, 20, 400); } function updateView(val) { viewX = val[0]; viewY = val[1]; } function onWindowResize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } function dehydratePing() { return new Uint8Array([PING_OP]).buffer; } function dehydrateRegMChunks(chunks) { const buffer = new ArrayBuffer(1 + 1 + chunks.length * 2); const view = new Uint16Array(buffer); // this will result into a double first byte, but still better than // shifting 16bit integers around later view[0] = REG_MCHUNKS_OP; for (let cnt = 0; cnt < chunks.length; cnt += 1) { view[cnt + 1] = chunks[cnt]; } return buffer; } function hydratePixelUpdate(data) { const i = data.getUint8(1); const j = data.getUint8(2); /* * offset and color of every pixel * 3 bytes offset * 1 byte color */ const pixels = []; let off = data.byteLength; while (off > 3) { const color = data.getUint8(off -= 1); const offsetL = data.getUint16(off -= 2); const offsetH = data.getUint8(off -= 1) << 16; pixels.push([offsetH | offsetL, color]); } return { i, j, pixels, }; } function onBinaryMessage(buffer) { if (buffer.byteLength === 0) return; const data = new DataView(buffer); const opcode = data.getUint8(0); if (opcode === PIXEL_UPDATE_OP || opcode === 145) { renderPixels(hydratePixelUpdate(data)); } } function dehydrateRegCanvas(canvasId) { const buffer = new ArrayBuffer(1 + 1); const view = new DataView(buffer); view.setInt8(0, REG_CANVAS_OP); view.setInt8(1, Number(canvasId)); return buffer; } function onMessage({data: message}) { try { if (typeof message !== 'string') { onBinaryMessage(message); } } catch (err) { console.error(`An error occurred while parsing websocket message ${message}`, err,); } } function socketConnect(i, url, allChunks) { const ws = new WebSocket(url); ws.binaryType = 'arraybuffer'; ws.onopen = () => { console.log(`Socket ${i} opened`); ws.send(dehydrateRegCanvas(0)); const chunkids = []; for (let j = 17000 * i; j < 17000 * (i + 1) && j < allChunks.length; j++) { chunkids.push(allChunks[j]); } ws.send(dehydrateRegMChunks(chunkids)); }; ws.onmessage = onMessage; ws.onclose = () => { console.log(`Socket ${i} closed`); setTimeout(() => { socketConnect(i, url, allChunks) }, 1000); }; ws.onerror = (err) => { console.error('Socket encountered error, closing socket', err); }; setInterval(() => { if (ws.readyState !== WebSocket.CLOSED) { ws.send(dehydratePing()); } }, 23000) } function radarMain() { canvas = document.createElement('canvas'); canvas.style.position = 'fixed'; canvas.style.top = '0'; canvas.style.left = '0'; canvas.style.zIndex = '0'; canvas.style.pointerEvents = 'none'; onWindowResize(); const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); document.body.appendChild(canvas); window.addEventListener('resize', onWindowResize); notifCircle = document.createElement('canvas'); notifCircle.width = 200; notifCircle.height = 200; const notifcontext = notifCircle.getContext('2d'); notifcontext.fillStyle = `rgba(255, 0, 0, 0.5)`; notifcontext.beginPath(); notifcontext.arc(100, 100, 100, 0, 2 * Math.PI); notifcontext.closePath(); notifcontext.fill(); pixelPlanetEvents.on('setscale', updateScale); pixelPlanetEvents.on('setviewcoordinates', updateView); setTimeout(render, 10); const url = `${ window.location.protocol === 'https:' ? 'wss:' : 'ws:' }//${ window.location.host }/ws`; const allChunks = [] for (let i = 0; i <= 255; i++) { for (let j = 0; j <= 255; j++) { allChunks.push((i << 8) | j); } } for (let i = 0; i < 4; i++) { setTimeout(() => { socketConnect(i, url, allChunks) }); } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址