FPS unlocker

Mope.io fps unlocker

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         FPS unlocker
// @namespace    http://tampermonkey.net/
// @version      2
// @description  Mope.io fps unlocker
// @author       Jerry || Discord: w5b
// @match        https://mope.io/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=mope.io
// @grant        unsafeWindow
// ==/UserScript==

const jerryPack = () => {
  let jerryData;
  loadStorage();
  checkForUpdate();
  const offsets = {
    fpsLimiter: jerryData.fpsLimit || "_0x39d3c4",
  };
  function checkForUpdate() {
    if (!jerryData.gameVersion || jerryData.gameVersion < $config.gameVersion) {
      jerryData.gameVersion = $config.gameVersion;

      fetch("https://mope.io/client.js")
        .then((res) => res.text())
        .then((text) => {
          const fpsLimitRegex = /requestAnimationFrame\(([^)]+)\)/;
          var fpsLimitOffset = text.match(fpsLimitRegex);
          if (fpsLimitOffset) {
            jerryData.fpsLimit = fpsLimitOffset[1];
          } else {
            console.dir("Regex match not found in the fetched content.");
          }
        })
        .catch((error) => {
          console.dir("Error fetching the client.js:", error);
        });
    }
  }

  function loadStorage() {
    try {
      jerryData = JSON.parse(localStorage.jerryDATA);
    } catch (error) {
      jerryData = {};
    }
  }

  function replaceAllFunctions() {
    unsafeWindow[offsets.fpsLimiter] = newFpsLimit;
  }

  let frameCount = 0,
    lastFrameTime = null,
    averageFPS = 0;

  function updateFPS(currentTime) {
    frameCount += 1;

    if (currentTime - lastFrameTime > 1000) {
      lastFrameTime = currentTime;
      averageFPS = frameCount;
      frameCount = 0;

      $bus["emit"]($bus["EVENTS"]["UI_SET_FPS"], averageFPS);
    }
  }

  let lastTime = 0;
  let targetFPS = jerryData.jerryFPS || 60;
  let timePerFrame = 1000 / targetFPS;

  function newFpsLimit(timestamp) {
    const elapsedTime = timestamp - lastTime;
    if (elapsedTime < timePerFrame) {
      requestAnimationFrame(unsafeWindow[offsets.fpsLimiter]);
      return;
    }
    updateFPS(timestamp);
    lastTime = timestamp;
    lastFrameTime === null && (lastFrameTime = timestamp);
    _0x2bcf6c(timestamp);
    requestAnimationFrame(newFpsLimit);
  }

  function handleFpsSlider() {
    const fpsSlider = document.getElementById("slider-fps");
    const sliderValue = document.getElementById("sliderValue");
    fpsSlider.value = jerryData.jerryFPS;
    sliderValue.innerHTML = fpsSlider.value;
    fpsSlider.addEventListener("input", function () {
      sliderValue.innerHTML = this.value;
      targetFPS = this.value;
      timePerFrame = 1000 / targetFPS;
    });
  }

  window.onbeforeunload = function () {
    const fpsSlider = document.getElementById("slider-fps");
    jerryData.jerryFPS = fpsSlider.value;
    localStorage.jerryDATA = JSON.stringify(jerryData);
  };

  document.addEventListener("keydown", (e) => {
    switch (e.key) {
      case "p":
        const mainContainer = document.getElementById("main-container");
        const currentDisplay = mainContainer.style.display;
        currentDisplay == "none"
          ? (mainContainer.style.display = "flex")
          : (mainContainer.style.display = "none");
        break;
    }
  });

  replaceAllFunctions();

  function injectHTML() {
    const htmlCode = `
      <!-- Paste your HTML code here -->
      <div id="main-container">
        <h1>FPS Unlocker</h1>
        <div class="slider-container">
          <input type="range" min="10" max="360" value="60" class="slider" id="slider-fps">
          <div id="slider-header-container">
          <p id="header-fps">FPS:  </p>
          <span contenteditable id="sliderValue">60</span>
          </div>
        </div>
        <div id="credits-container">
        <button id="credits-button">Join Discord</button>
        </div>
        </div>
    `;

    const div = document.createElement("div");
    div.innerHTML = htmlCode;

    document.body.appendChild(div);

    const sliderValue = document.getElementById("sliderValue");

    sliderValue.addEventListener("keydown", function (event) {
      if (event.key === "Enter") {
        this.blur();
        event.preventDefault();
      }
    });

    sliderValue.addEventListener("blur", function () {
      let newValue = parseInt(this.textContent);

      if (!isNaN(newValue)) {
        const fpsSlider = document.getElementById("slider-fps");

        newValue = Math.min(
          Math.max(newValue, parseInt(fpsSlider.min)),
          parseInt(fpsSlider.max)
        );

        this.textContent = newValue;
        fpsSlider.value = newValue;
      }
    });

    const mainContainer = document.getElementById("main-container");
    makeDraggable(mainContainer);

    document.getElementById("credits-button").addEventListener("click", function() {
      window.open("https://discord.gg/NSyHaFG8Uv", "_blank");
    });
  }

  function makeDraggable(element) {
    let offsetX, offsetY;
    let isDragging = false;

    element.addEventListener("mousedown", function (e) {
      const rect = element.getBoundingClientRect();
      const mainContainer = document.getElementById("main-container");

      if (e.target == mainContainer) {
        e.preventDefault();
        isDragging = true;

        function moveElement(e) {
          if (!isDragging) return;
          element.style.position = "absolute";
          element.style.left = e.clientX + "px";
          element.style.top = e.clientY + "px";
        }

        function stopDragging() {
          isDragging = false;
          document.removeEventListener("mousemove", moveElement);
          document.removeEventListener("mouseup", stopDragging);
        }

        document.addEventListener("mousemove", moveElement);
        document.addEventListener("mouseup", stopDragging);
      }
    });
  }

  function injectCSS() {
    const style = document.createElement("style");
    style.textContent = `
      h1 {
        color: white;
        margin: 20px;
        text-shadow: 1px 1px 1px black;
        font-family: sans-serif
      }

      #main-container {
        background-color: rgba(0, 0, 0, 0.7);
        border-radius: 2%;
        width: 23vw;
        height: 20vh;
        position: absolute;
        top: 20%;
        left: 80%;
        transform: translate(-50%, -50%);
        display: flex;
        align-items: center;
        flex-direction: column;
        z-index: 999
      }

      .slider-container {
        display: flex;
        flex-direction: column;
        justify-content: center;
        width: 100%;
        align-items: center;
      }

      #main-container p,span {
        color: white;
        font-weight: bold;
        text-shadow: 1px 1px 1px black;
        font-family: sans-serif
      }

      .slider {
        width: 70%;
        background-color: rgba(0, 0, 0, 0.2);
        height: 50%;
        -webkit-appearance: none;
        appearance: none;
        border-radius: 16px;
        overflow: hidden;
        border-radius: 16px;
      }

      .slider::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 25px;
        height: 25px;
        background-color: #fff;
        border-radius: 50%;
        box-shadow: -407px 0 0 400px black;
        transition: 0.2s ease-in-out;
      }

      .slider::-moz-range-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 25px;
        height: 25px;
        background-color: #fff;
        border-radius: 50%;
        box-shadow: -407px 0 0 400px black;
        transition: 0.2s ease-in-out;
      }

      #slider-header-container {
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items:center;
      }

      #sliderValue {
        margin: 5px;
      }

      #credits-container {
        display: flex;
        position: absolute;
        justify-content: flex-end;
        align-items: center;
        right: 10px;
        width: 100%;
        height: 30px;
        bottom: 10px;
        }
        #credits-button {
          text-decoration: none;
          color: white;
          padding: 5px;
          background-color: transparent;
          border-radius: 5px;
          border: none;
          font-weight: bolder;
          font-family: sans-serif;
          cursor: pointer;
          font-size: 15px;
          text-shadow: 1px 1px 1px black;
          }

          #credits-button:hover {
            font-size: 16px;
            transition: font-size 0.1s ease-out
          }
    `;
    document.head.appendChild(style);
  }

  injectHTML();
  injectCSS();
  handleFpsSlider();
};
const initInterval = setInterval(() => {
  if (document.querySelector("head > script:nth-child(55)")) {
    clearInterval(initInterval);
    jerryPack();
  }
}, 100);