Greasy Fork

Greasy Fork is available in English.

vanis X gota

https://discord.gg/UsPCMBDGES

当前为 2022-10-20 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         vanis X gota
// @namespace    http://tampermonkey.net/
// @version      1.8
// @description  https://discord.gg/UsPCMBDGES
// @author       Eva#5121 YaoYT#4707
// @match        *://vanis.io/*
// @compatible     chrome
// @compatible     opera
// @compatible     firefox
// @icon         https://cdn.discordapp.com/attachments/891105311434367076/972941615607984128/60234575-fondo-rojo-solido-o-papel-rojo-con-textura-de-fondo-para-el-diseno-del-dia-de-san-valentin-o-fondo-d11.jpg
// @require http://code.jquery.com/jquery-3.3.1.min.js
// @require https://code.jquery.com/ui/1.12.0/jquery-ui.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery-confirm/3.3.0/jquery-confirm.min.js
// @run-at       document-start
// @grant        unsafeWindow

// @license N/A
// ==/UserScript==


(function() {
    var local = {
        SCRIPT_CONFIG: {
            
        },
        MENU_CONFIG: {



            COLOR_1: "#0A2A64",
            COLOR_2:"#0A2A64",
            
        },


        COLOR_HUE: 100,
        COLOR_HUE2: 300,
        GAME_WS: false,
        GAME_INIT: false,
        PLAYER_PACKET_SPAWN: [],
        PLAYER_SOCKET: false,
        PLAYER_IS_DEAD: false,
        PLAYER_MOUSE: {
            x: false,
            y: false,
        },
        GAME_BYPASS: {
            mouseFrozen: Symbol(),
            utf8: new TextEncoder()
        }
    }

    function changeHue() {
        355 == local.COLOR_HUE && (local.COLOR_HUE = 100), local.COLOR_HUE++;
        355 == local.COLOR_HUE2 && (local.COLOR_HUE2 = 100), local.COLOR_HUE2++;
        $('.fade-box').css({
            background: 'linear-gradient(to right bottom,hsl('+local.COLOR_HUE+', 50%, 10%),hsl('+local.COLOR_HUE2+', 50%, 10%)'
        })
    }
    function ready() {
        setInterval(() => {
            if(local.MENU_CONFIG.RAINBOW) {
                changeHue()
            } else {
                $('.fade-box').css({
                    background: `linear-gradient(to right bottom,${local.MENU_CONFIG.COLOR_1},${local.MENU_CONFIG.COLOR_2})`
                })
            }
        }, 10)
    }
    const { fillText } = CanvasRenderingContext2D.prototype;
    CanvasRenderingContext2D.prototype.fillText = function(text, x, y) {
        let config = local.SCRIPT_CONFIG
        if(text == document.getElementById("nickname").value) {
            this.fillStyle = config.NAME_COLOR;
        }
        fillText.call(this, ...arguments);
    }
    document.addEventListener("DOMContentLoaded", ready)

})();










class Reader {
  offset = 0;

  constructor(arrayBuffer, LE = true) {
    this.view = new DataView(arrayBuffer);
    this.offset = 0;
    this.LE = LE;
  }

  skip = bytes => ((this.offset += bytes), this);
  readUint8 = () => this.view.getUint8(this.offset++);
  readUint16 = LE => ((this.offset += 2), this.view.getUint16(this.offset - 2, LE ?? this.LE));
  readUint32 = LE => ((this.offset += 4), this.view.getUint32(this.offset - 4, LE ?? this.LE));
  readInt8 = () => this.view.getInt8(this.offset++);
  readInt16 = LE => ((this.offset += 2), this.view.getInt16(this.offset - 2, LE ?? this.LE));
  readInt32 = LE => ((this.offset += 4), this.view.getInt32(this.offset - 4, LE ?? this.LE));
  readFloat32 = LE => ((this.offset += 4), this.view.getFloat32(this.offset - 4, LE ?? this.LE));
  readFloat64 = LE => ((this.offset += 8), this.view.getFloat64(this.offset - 8, LE ?? this.LE));
  readBytes = length => ((this.offset += length), this.view.buffer.slice(this.offset - length, this.offset));

  readString(unicode = false) {
    const method = unicode ? this.readUint16 : this.readUint8;
    let string = '';
    let charCode = method();

    while (charCode !== 0) {
      string += String.fromCharCode(charCode);
      charCode = method();
    }

    return string;
  }
}
class Writer {
  constructor(length, offset = 0) {
    this.view = new DataView(new ArrayBuffer(length));
    this.offset = offset;
  }

  skip = bytes => ((this.offset += bytes), this);
  writeUint8 = value => (this.view.setUint8(this.offset++, value), this);
  writeUint16 = value => ((this.offset += 2), this.view.setUint16(this.offset - 2, value, true), this);
  writeUint32 = value => ((this.offset += 4), this.view.setUint32(this.offset - 4, value, true), this);
  writeInt8 = value => (this.view.setInt8(this.offset++, value), this);
  writeInt16 = value => ((this.offset += 2), this.view.setInt16(this.offset - 2, value, true), this);
  writeInt32 = value => ((this.offset += 4), this.view.setInt32(this.offset - 4, value, true), this);
  writeFloat32 = value => ((this.offset += 4), this.view.setFloat32(this.offset - 4, value, true), this);
  writeFloat64 = value => ((this.offset += 8), this.view.setFloat64(this.offset - 8, value, true), this);

  writeString(string, unicode = false) {
    const method = unicode ? this.writeUint16 : this.writeUint8;
    Array.from(string).forEach(char => method(char.charCodeAt(0)));
    method(0);
    return this;
  }

  get raw() {
    return this.view;
  }
}

/**
 *
 * @param {keyof HTMLElementTagNameMap} el element tag name e.g. 'div'
 * @param {{ [key:string]: string }} attrs attributes array to be applied to new element
 * @param  {...(string|Node)[]} append children to apply
 * @returns {HTMLElement} HTMLElement
 */
function newElement(el, attrs = {}, ...append) {
  const element = document.createElement(el);

  Object.entries(attrs).forEach(([key, val]) => element.setAttribute(key, val));

  element.append(...append);

  return element;
}

const WebSocket = window.WebSocket;

const partySpan = newElement('span', {}, 'YaoYT Party');
const totalMass = newElement('span', {}, '0');
const title = newElement(
  'div',
  { style: `display:flex;flex-direction:row;justify-content:space-between;flex:1;font-weight:bold;margin-bottom:5px;letter-spacing:1px;` },
  partySpan,
  totalMass
);

const members = newElement('div', { style: `display:flex;flex-direction:column;font-size:12px` });

const hud = newElement(
  'div',
  {
    style: `white-space:nowrap;display:flex;flex-direction:column;position:absolute;top:230px;background:rgba(0,0,0,.45);border-radius:4px;pointer-events:none;border:1px solid white;padding:5px;min-width:150px;left:5px;box-sizing:border-box;font-size:15px;`,
  },
  title,
  members
);

const createMember = (nickname, mass, color) =>
  newElement(
    'div',
    { style: `display:flex;flex-direction:row;justify-content:space-between;flex:1` },
    newElement('span', { style: `margin-right: 15px;color:${color}` }, nickname),
    newElement('span', {}, mass)
  );
const formatUrl = url => url.replace('wss://', '').replace(/\/$/, '');

class ApiConnection {
  constructor(_) {
    this._ = _;
    this.toast = Swal.mixin({ toast: true, position: 'top', showConfirmButton: false, showCloseButton: true });
    this.v = 1;
    this.url = 'wss://vanis-parties.herokuapp.com';
    this.massMap = new Map();
    /** @type {WebSocket} */
    this.ws = null;
    this.interval = 0;
    this.lastBoop = 0;

    this.idlePacket = new Writer(2 + 4 + 8 + 1).writeUint8(2).writeString('null').writeUint16(0).writeUint16(0).writeUint32(0).writeUint8(0).raw.buffer;
    this.boot();
  }

  get account() {
    return this._.app.$children[6].$children[2].account;
  }
  get connected() {
    return this.interval !== 0;
  }
  get allowed() {
    return this._ && this.account?.discord_id;
  }

  getMass(pid) {
    const mass = this.massMap.get(pid);
    if (mass === -1) return 'SPEC';
    if (mass === 0) return 'You are dead';
    return mass ?? '????';
  }

  head = () => fetch(this.url.replace('wss', 'https').replace('ws', 'http'), { method: 'HEAD' });

  boot = () => {
    if (!this.allowed) {
      members.textContent = 'Not logged in, retrying...';
      return setTimeout(this.boot, 1000);
    }
    members.textContent = 'Waking up the server';

    this.head()
      .then(() => this.connect())
      .catch(() => {
        members.textContent = 'Waking up failed, re-trying';
        setTimeout(this.boot, 1000);
      });
  };

  connect = () => {
    if (!this.allowed) {
      members.textContent = 'Not logged in, retrying...';
      return setTimeout(this.connect, 1000);
    }
    members.textContent = 'Connecting to the server';
    this.ws = new WebSocket(this.url);
    this.ws.binaryType = 'arraybuffer';
    this.ws.onopen = this.onopen;
    this.ws.onclose = this.onclose;
    this.ws.onmessage = ({ data }) => this.onmessage(new Reader(data));
  };

  onopen = () => {
    const id = this.account?.discord_id;
    if (!id) {
      members.textContent = 'Not logged in';
      return this.ws.close(4000);
    }
    members.textContent = 'Logging in...';
    this.ws.send(new Writer(3 + id.length).writeUint8(1).writeString(id).writeUint8(this.v).raw.buffer);
  };

  onclose = e => {
    clearInterval(this.interval);
    this.interval = 0;

    if (e.code === 1008) {
      members.textContent = 'Update required';
      return this.toast.fire({ title: '[Parties] Old script version, please update', type: 'error' });
    }

    if (e.code === 1002 || e.code === 1003) {
      members.textContent = 'Server is updating or new version is coming';
      return this.toast.fire({ title: '[Parties] Protocol error\nWait for new version or a server fix', type: 'error' });
    }

    members.textContent = 'Disconnected, retrying';
    setTimeout(this.connect, 1000);

    console.log('you are offline', e);
  };
  /** @param {Reader} data */
  onmessage = data => {
    const opcode = data.readUint8();

    switch (opcode) {
      case 1: {
        this.interval = setInterval(this.sendData, 888);
        this.lastBoop = Date.now();
        members.textContent = '';
        break;
      }
      case 2: {
        this.massMap.clear();

        let amount = data.readUint8();
        while (--amount !== -1) {
          const [pid, mass, flags] = [data.readUint16(), data.readUint32(), data.readUint8()];
          if (flags & 1) this.massMap.set(pid, -1);
          else this.massMap.set(pid, mass);
        }

        break;
      }
      default: {
        console.log('invalid opcode received: ' + opcode);
      }
    }
  };

  sendData = () => {
    // keep free tier server alive because apparently alive websocket connections are not a good reason to not shut down due to inactivity
    if (this.lastBoop + 10 * 60 * 1000 <= Date.now()) {
      const temp = this.lastBoop;
      this.lastBoop = Date.now(); // incase the next sendData is called before this request is done
      this.head()
        .then(() => console.log('booped successfully'))
        .catch(e => console.log('boop failed', e, (this.lastBoop = temp + 60000)));
    }

    if (!this._) return;
    if (!this._.ws || this._.ws.readyState !== WebSocket.OPEN || !this._.activePid) return void this.ws.send(this.idlePacket);

    const url = formatUrl(this._.ws.url);
    const tagId = this._.tagId || 0;

    const pid = this._.pid || this._.activePid;
    const mass = this._.score || 0;
    const flags = Number(this._.spectating);

    const multi = this._.minion;
    const summed = settings.minionMode === false && !!multi && multi.mass > 0 && mass > 0;

    const sendMass = Math.max(0, summed ? mass - multi.mass : mass);

    const data = new Writer(2 + url.length + 2 + (2 + 4 + 1) * (multi ? 2 : 1))
      .writeUint8(2)
      .writeString(url)
      .writeUint16(tagId)
      .writeUint16(pid)
      .writeUint32(sendMass)
      .writeUint8(flags);
    if (multi) {
      data
        .writeUint16(multi.pid || 0)
        .writeUint32(multi.mass || 0)
        .writeUint8(0);
    }

    this.ws.send(data.raw.buffer);
  };
}

const init = _ => {
  document.getElementById('hud').append(hud);

  const partyConnection = new ApiConnection(_);
  _.partyConnection = partyConnection;

  const tick = () => {
    if (!partyConnection.connected || !_) return;
    if (!_?.playerManager?.players) return (members.textContent = 'Not on a server');
    if (!_.tagId) return (members.textContent = 'you must be in a tag');
    members.textContent = '';

    while (members.children.length) members.removeChild(members.firstChild);

    let massSum = 0;
    const elements = Object.values(_.playerManager.players)
      .filter(player => player.tagId === _.tagId)
      .map(({ pid, name, nameColorCss }) => {
        const color = nameColorCss || '#FFF000';
        const mass = partyConnection.getMass(pid);
        if (typeof mass === 'number') massSum += mass;
        return createMember(name, mass, color);
      });

    totalMass.textContent = massSum;

    members.append(...elements);
  };

  tick();
  setInterval(tick, 1000);
};

const c = Function.prototype.call;
Function.prototype.call = function (t, ...a) {
  if (t === a[1] && typeof a[2] === 'function') {
    const e = this;

    function f() {
      e.apply(this, arguments);

      members.textContent = 'Please log in';
      const _ = arguments[2](1);
      const g = _.app.$children[6].$children[2].setAccountData;
      _.app.$children[6].$children[2].setAccountData = function () {
        init(_);
        _.app.$children[6].$children[2].setAccountData = g;
        return g.apply(this, arguments);
      };
    }

    Function.prototype.call = c;
    return c.apply(f, arguments);
  }
  return c.apply(this, arguments);
};