- // ==UserScript==
- // @name RoLocate
- // @namespace https://oqarshi.github.io/
- // @version 21.3
- // @description Adds filter options to roblox server page. Alternative to paid extensions like RoPro, RoGold (Ultimate), RoQol, and RoKit.
- // @author Oqarshi
- // @match https://www.roblox.com/games/*
- // @license MIT
- // @icon data:image/jpeg;base64,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
- // @grant GM_xmlhttpRequest
- // ==/UserScript==
-
-
-
- (function() {
- 'use strict';
-
- /*********************************************************************************************************************************************************************************************************************************************
- This is all of the functions for the filter button and the popup for the 8 buttons does not include the functions for the 8 buttons
-
- *********************************************************************************************************************************************************************************************************************************************/
-
- function createPopup() {
- const popup = document.createElement('div');
- popup.className = 'server-filters-dropdown-box'; // Unique class name
- popup.style.cssText = `
- position: absolute;
- width: 210px;
- height: 382px;
- right: 0px;
- top: 30px;
- z-index: 1000;
- border-radius: 5px;
- background-color: rgb(30, 32, 34);
- display: flex;
- flex-direction: column;
- padding: 5px;
- `;
-
- // Create the header section
- const header = document.createElement('div');
- header.style.cssText = `
- display: flex;
- align-items: center;
- padding: 10px;
- border-bottom: 1px solid #444;
- margin-bottom: 5px;
- `;
-
- // Add the logo (base64 image)
- const logo = document.createElement('img');
- logo.src = 'data:image/jpeg;base64,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'; // Replace with your base64 logo
- logo.style.cssText = `
- width: 24px;
- height: 24px;
- margin-right: 10px;
- `;
-
- // Add the title
- const title = document.createElement('span');
- title.textContent = 'RoLocate';
- title.style.cssText = `
- color: white;
- font-size: 18px;
- font-weight: bold;
- `;
-
- // Append logo and title to the header
- header.appendChild(logo);
- header.appendChild(title);
-
- // Append the header to the popup
- popup.appendChild(header);
-
- // Define unique names and tooltips for each button
- const buttonData = [{
- name: "Smallest Servers",
- tooltip: "**Reverses the order of the server list.** The emptiest servers will be displayed first."
- },
- {
- name: "Available Space",
- tooltip: "**Filters out servers which are full.** Servers with space will only be shown."
- },
- {
- name: "Player Count",
- tooltip: "**Roblox Locator finds servers with your specified player count or fewer.** Searching for up to 3 minutes. If no exact match is found, it shows servers closest to the target."
- },
- {
- name: "Random Shuffle",
- tooltip: "**Display servers in a completely random order.** Shows servers with space and servers with low player counts in a randomized order."
- },
- {
- name: "Server Region",
- tooltip: "**Filters servers by region.** Offering more accuracy than 'Best Connection' in areas with fewer Roblox servers, like India, or in games with high player counts."
- },
- {
- name: "Best Connection",
- tooltip: "**Automatically joins the fastest servers for you.** However, it may be less accurate in regions with fewer Roblox servers, like India, or in games with large player counts."
- },
- {
- name: "Auto Join Small Server",
- tooltip: "**Automatically tries to join a server with a very low population.** On popular games servers may fill up very fast so you might not always get in alone."
- },
- {
- name: "About",
- tooltip: "**The Credits.** Rolocate was created by Oqarshi. Special thanks to BTRoblox ❤️. Enjoy using Rolocate!"
- }
- ];
-
- // Create buttons with unique names and tooltips
- buttonData.forEach((data, index) => {
- const buttonContainer = document.createElement('div');
- buttonContainer.className = 'server-filter-option';
- buttonContainer.style.cssText = `
- width: 190px;
- height: 30px;
- background-color: #393B3D;
- margin: 5px;
- border-radius: 5px;
- padding: 3.5px;
- position: relative;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: background-color 0.3s ease;
- `;
-
- const tooltip = document.createElement('div');
- tooltip.className = 'filter-tooltip';
- tooltip.style.cssText = `
- display: none;
- position: absolute;
- top: -10px;
- left: 200px;
- width: auto;
- inline-size: 200px;
- height: auto;
- background-color: #191B1D;
- color: white;
- padding: 5px;
- border-radius: 5px;
- white-space: pre-wrap;
- font-size: 14px;
- `;
-
- // Parse tooltip text and replace **...** with bold HTML tags
- tooltip.innerHTML = data.tooltip.replace(/\*\*(.*?)\*\*/g, "<b style='color: #068f00;'>$1</b>");
-
- const buttonText = document.createElement('p');
- buttonText.style.cssText = `
- margin: 0;
- color: white;
- font-size: 16px;
- `;
- buttonText.textContent = data.name;
-
- buttonContainer.appendChild(tooltip);
- buttonContainer.appendChild(buttonText);
-
- buttonContainer.addEventListener('mouseover', () => {
- tooltip.style.display = 'block';
- buttonContainer.style.backgroundColor = '#4A4C4E'; // Hover effect
- });
- buttonContainer.addEventListener('mouseout', () => {
- tooltip.style.display = 'none';
- buttonContainer.style.backgroundColor = '#393B3D'; // Revert to original color
- });
-
- buttonContainer.addEventListener('click', () => {
- switch (index) {
- case 0:
- smallest_servers();
- break;
- case 1:
- available_space_servers();
- break;
- case 2:
- player_count_tab();
- break;
- case 3:
- random_servers();
- break;
- case 4:
- createServerCountPopup((totalLimit) => {
- rebuildServerList(gameId, totalLimit);
- });
- break;
- case 5:
- rebuildServerList(gameId, 50, true);
- break;
- case 6:
- auto_join_small_server();
- break;
- case 7:
- credits();
- break;
- }
- });
-
- popup.appendChild(buttonContainer);
- });
-
- return popup;
- }
-
-
- /*******************************************************
- name of function: An Observer for the filter button
- description: to put the filter button on the page
- *******************************************************/
-
- // Wait for the server list options container to load
- const observer = new MutationObserver((mutations, obs) => {
- const serverListOptions = document.querySelector('.server-list-options');
- if (serverListOptions) {
- // Create the filter button
- const filterButton = document.createElement('a');
- filterButton.className = 'RL-filter-button'; // Unique class name
- filterButton.style.cssText = `
- color: white;
- font-weight: bold;
- text-decoration: none;
- cursor: pointer;
- margin-left: 10px;
- padding: 5px 10px;
- display: flex;
- align-items: center;
- gap: 5px;
- position: relative;
- margin-top: 4px
- `;
- filterButton.addEventListener('mouseover', () => {
- filterButton.style.textDecoration = 'underline';
- });
- filterButton.addEventListener('mouseout', () => {
- filterButton.style.textDecoration = 'none';
- });
-
- // Add the "Filter" text
- const buttonText = document.createElement('span');
- buttonText.className = 'RL-filter-text'; // Unique class name
- buttonText.textContent = 'Filters';
- filterButton.appendChild(buttonText);
-
- // Add the icon (three horizontal dashes)
- const icon = document.createElement('span');
- icon.className = 'RL-filter-icon'; // Unique class name
- icon.textContent = '≡';
- icon.style.cssText = `
- font-size: 18px;
- `;
- filterButton.appendChild(icon);
-
- // Append the button to the server list options container
- serverListOptions.appendChild(filterButton);
-
- // Handle click event to show/hide the popup
- let popup = null;
- filterButton.addEventListener('click', (event) => {
- event.stopPropagation(); // Prevent event bubbling
- if (popup) {
- popup.remove(); // Remove the popup if it already exists
- popup = null;
- } else {
- popup = createPopup();
- // Position the popup next to the filter button
- popup.style.top = `${filterButton.offsetHeight}px`;
- popup.style.left = '0';
- filterButton.appendChild(popup);
- }
- });
-
- // Close the popup when clicking outside
- document.addEventListener('click', (event) => {
- if (popup && !filterButton.contains(event.target)) {
- popup.remove();
- popup = null;
- }
- });
-
- // Stop observing once the button is added
- obs.disconnect();
- }
- })
-
- /*********************************************************************************************************************************************************************************************************************************************
- The End of: This is all of the functions for the filter button and the popup for the 8 buttons does not include the functions for the 8 buttons
-
- *********************************************************************************************************************************************************************************************************************************************/
-
-
- /*********************************************************************************************************************************************************************************************************************************************
- Functions for the 1st button
-
- *********************************************************************************************************************************************************************************************************************************************/
-
-
- /*******************************************************
- name of function: smallest_servers FIRST FUNCTION
- description: Fetches the smallest servers, disables the "Load More" button, shows a loading bar, and recreates the server cards.
- *******************************************************/
- async function smallest_servers() {
- // Disable the "Load More" button and show the loading bar
- Loadingbar(true);
- disableFilterButton(true);
- disableLoadMoreButton();
-
- // Get the game ID from the URL
- const gameId = window.location.pathname.split('/')[2];
-
- // Retry mechanism
- let retries = 3;
- let success = false;
-
- while (retries > 0 && !success) {
- try {
- // Fetch server data from the Roblox API
- const response = await fetch(`https://games.roblox.com/v1/games/${gameId}/servers/0?sortOrder=1&excludeFullGames=true&limit=100`);
-
- // Check if the response status is 429 (Too Many Requests)
- if (response.status === 429) {
- throw new Error('429: Too Many Requests');
- }
-
- const data = await response.json();
-
- // Process each server
- for (const server of data.data) {
- const {
- id: serverId,
- playerTokens,
- maxPlayers,
- playing
- } = server;
-
- // Pass the server data to the card creation function
- await rbx_card(serverId, playerTokens, maxPlayers, playing, gameId);
- }
-
- success = true; // Mark as successful if no errors occurred
- } catch (error) {
- retries--; // Decrement the retry count
-
- if (error.message === '429: Too Many Requests' && retries > 0) {
- console.log('Encountered a 429 error. Retrying in 10 seconds...');
- await new Promise(resolve => setTimeout(resolve, 10000)); // Wait for 10 seconds
- } else {
- console.error('Error fetching server data:', error);
- break; // Exit the loop if it's not a 429 error or no retries left
- }
- } finally {
- if (success || retries === 0) {
- // Hide the loading bar and enable the filter button
- Loadingbar(false);
- disableFilterButton(false);
- }
- }
- }
- }
-
-
-
- /*********************************************************************************************************************************************************************************************************************************************
- Functions for the 2nd button
-
- *********************************************************************************************************************************************************************************************************************************************/
-
-
- /*******************************************************
- name of function: available_space_servers
- description: Fetches servers with available space, disables the "Load More" button, shows a loading bar, and recreates the server cards.
- *******************************************************/
- async function available_space_servers() {
- // Disable the "Load More" button and show the loading bar
- Loadingbar(true);
- disableLoadMoreButton();
- disableFilterButton(true);
-
- // Get the game ID from the URL
- const gameId = window.location.pathname.split('/')[2];
-
- // Retry mechanism
- let retries = 3;
- let success = false;
-
- while (retries > 0 && !success) {
- try {
- // Fetch server data from the Roblox API
- const response = await fetch(`https://games.roblox.com/v1/games/${gameId}/servers/0?sortOrder=2&excludeFullGames=true&limit=100`);
-
- // Check if the response status is 429 (Too Many Requests)
- if (response.status === 429) {
- throw new Error('429: Too Many Requests');
- }
-
- const data = await response.json();
-
- // Process each server
- for (const server of data.data) {
- const {
- id: serverId,
- playerTokens,
- maxPlayers,
- playing
- } = server;
-
- // Pass the server data to the card creation function
- await rbx_card(serverId, playerTokens, maxPlayers, playing, gameId);
- }
-
- success = true; // Mark as successful if no errors occurred
- } catch (error) {
- retries--; // Decrement the retry count
-
- if (error.message === '429: Too Many Requests' && retries > 0) {
- console.log('Encountered a 429 error. Retrying in 10 seconds...');
- await new Promise(resolve => setTimeout(resolve, 10000)); // Wait for 10 seconds
- } else {
- console.error('Error fetching server data:', error);
- break; // Exit the loop if it's not a 429 error or no retries left
- }
- } finally {
- if (success || retries === 0) {
- // Hide the loading bar and enable the filter button
- Loadingbar(false);
- disableFilterButton(false);
- }
- }
- }
- }
-
- /*********************************************************************************************************************************************************************************************************************************************
- Functions for the 3rd button
-
- *********************************************************************************************************************************************************************************************************************************************/
-
-
- /*******************************************************
- name of function: player_count_tab
- description: Opens a popup for the user to select the max player count using a slider and filters servers accordingly.
- *******************************************************/
- function player_count_tab() {
- // Create the popup container
- const popup = document.createElement('div');
- popup.className = 'player-count-popup';
- popup.style.cssText = `
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background-color: rgb(30, 32, 34);
- padding: 20px;
- border-radius: 5px;
- z-index: 10000;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 10px;
- `;
-
- // Add a title
- const title = document.createElement('h3');
- title.textContent = 'Select Max Player Count';
- title.style.cssText = `
- color: white;
- margin: 0;
- font-size: 18px;
- `;
- popup.appendChild(title);
-
- // Add a slider
- const slider = document.createElement('input');
- slider.type = 'range';
- slider.min = '1';
- slider.max = '100';
- slider.value = '1'; // Default value
- slider.style.cssText = `
- width: 200px;
- cursor: pointer;
- `;
- popup.appendChild(slider);
-
- // Add a display for the slider value
- const sliderValue = document.createElement('span');
- sliderValue.textContent = slider.value;
- sliderValue.style.cssText = `
- color: white;
- font-size: 16px;
- `;
- popup.appendChild(sliderValue);
-
- // Update the slider value display when the slider changes
- slider.addEventListener('input', () => {
- sliderValue.textContent = slider.value;
- });
-
- // Add a submit button
- const submitButton = document.createElement('button');
- submitButton.textContent = 'Search';
- submitButton.style.cssText = `
- padding: 5px 10px;
- font-size: 16px;
- background-color: #00A2FF;
- color: white;
- border: none;
- border-radius: 3px;
- cursor: pointer;
- `;
- popup.appendChild(submitButton);
-
- // Add a close button
- const closeButton = document.createElement('button');
- closeButton.textContent = 'Close';
- closeButton.style.cssText = `
- padding: 5px 10px;
- font-size: 16px;
- background-color: #555;
- color: white;
- border: none;
- border-radius: 3px;
- cursor: pointer;
- `;
- popup.appendChild(closeButton);
-
- // Append the popup to the body
- document.body.appendChild(popup);
-
- // Handle submit button click
- submitButton.addEventListener('click', () => {
- const maxPlayers = parseInt(slider.value, 10);
- if (!isNaN(maxPlayers) && maxPlayers > 0) {
- filterServersByPlayerCount(maxPlayers);
- popup.remove();
- } else {
- notifications('Error: Please enter a number greater than 0', 'error', '⚠️');
- }
- });
- // Handle close button click
- closeButton.addEventListener('click', () => {
- popup.remove();
- });
-
- // Close the popup when clicking outside
- document.addEventListener('click', (event) => {
- if (!popup.contains(event.target)) {
- popup.remove();
- }
- });
- }
-
- /*******************************************************
- name of function: fetchServersWithRetry
- description: Fetches server data with retry logic and a delay between requests to avoid rate-limiting.
- Uses GM_xmlhttpRequest instead of fetch.
- *******************************************************/
- async function fetchServersWithRetry(url, retries = 15, currentDelay = 750) {
- return new Promise((resolve, reject) => {
- GM_xmlhttpRequest({
- method: 'GET',
- url: url,
- onload: function(response) {
- // Check for 429 Rate Limit error
- if (response.status === 429) {
- if (retries > 0) {
- const newDelay = currentDelay * 1; // Exponential backoff
- console.log(`[DEBUG] Rate limited. Waiting ${newDelay / 1000} seconds before retrying...`);
- setTimeout(() => {
- resolve(fetchServersWithRetry(url, retries - 1, newDelay)); // Retry with increased delay
- }, newDelay);
- } else {
- console.error('[DEBUG] Rate limit retries exhausted.');
- notifications('Error: Rate limited please try again later.', 'error', '⚠️')
- reject(new Error('RateLimit'));
- }
- return;
- }
-
- // Handle other HTTP errors
- if (response.status < 200 || response.status >= 300) {
- console.error('[DEBUG] HTTP error:', response.status, response.statusText);
- reject(new Error(`HTTP error: ${response.status}`));
- return;
- }
-
- // Parse and return the JSON data
- try {
- const data = JSON.parse(response.responseText);
- console.log('[DEBUG] Fetched data successfully:', data);
- resolve(data);
- } catch (error) {
- console.error('[DEBUG] Error parsing JSON:', error);
- reject(error);
- }
- },
- onerror: function(error) {
- console.error('[DEBUG] Error in GM_xmlhttpRequest:', error);
- reject(error);
- }
- });
- });
- }
-
- /*******************************************************
- name of function: filterServersByPlayerCount
- description: Filters servers to show only those with a player count equal to or below the specified max.
- If no exact matches are found, prioritizes servers with player counts lower than the input.
- Keeps fetching until at least 8 servers are found, with a dynamic delay between requests.
- *******************************************************/
- async function filterServersByPlayerCount(maxPlayers) {
- // Validate maxPlayers before proceeding
- if (isNaN(maxPlayers) || maxPlayers < 1 || !Number.isInteger(maxPlayers)) {
- console.error('[DEBUG] Invalid input for maxPlayers.');
- notifications('Error: Please input a valid whole number greater than or equal to 1.', 'error', '⚠️');
- return;
- }
-
- // Disable UI elements and clear the server list
- Loadingbar(true);
- disableLoadMoreButton();
- disableFilterButton(true);
- const serverList = document.querySelector('#rbx-game-server-item-container');
- serverList.innerHTML = '';
-
- const gameId = window.location.pathname.split('/')[2];
- let cursor = null;
- let serversFound = 0;
- let serverMaxPlayers = null;
- let isCloserToOne = null;
- let topDownServers = []; // Servers collected during top-down search
- let bottomUpServers = []; // Servers collected during bottom-up search
- let currentDelay = 500; // Initial delay of 0.5 seconds
- const timeLimit = 3 * 60 * 1000; // 3 minutes in milliseconds
- const startTime = Date.now(); // Record the start time
- notifications('Will search for a maximum of 3 minutes to find a server.', 'success', '🔎');
-
-
- try {
- while (serversFound < 16) {
- // Check if the time limit has been exceeded
- if (Date.now() - startTime > timeLimit) {
- console.log('[DEBUG] Time limit reached. Proceeding to fallback servers.');
- notifications('Warning: Time limit reached. Proceeding to fallback servers.', 'warning', '❗');
- break;
- }
-
- // Fetch initial data to determine serverMaxPlayers and isCloserToOne
- if (!serverMaxPlayers) {
- const initialUrl = cursor ?
- `https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=100&cursor=${cursor}` :
- `https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=100`;
-
- const initialData = await fetchServersWithRetry(initialUrl);
- if (initialData.data.length > 0) {
- serverMaxPlayers = initialData.data[0].maxPlayers;
- isCloserToOne = maxPlayers <= (serverMaxPlayers / 2);
- } else {
- console.error('[DEBUG] No servers found in initial fetch.');
- break;
- }
- }
-
- // Validate maxPlayers against serverMaxPlayers
- if (maxPlayers >= serverMaxPlayers) {
- console.error('[DEBUG] Invalid input: maxPlayers is greater than or equal to serverMaxPlayers.');
- notifications(`Error: Please input a number between 1 through ${serverMaxPlayers - 1}`, 'error', '⚠️');
- return;
- }
-
- // Adjust the URL based on isCloserToOne
- const baseUrl = isCloserToOne ?
- `https://games.roblox.com/v1/games/${gameId}/servers/public?sortOrder=1&excludeFullGames=true&limit=100` :
- `https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=100`; // why does this work lmao
-
- const url = cursor ? `${baseUrl}&cursor=${cursor}` : baseUrl;
- const data = await fetchServersWithRetry(url);
-
- // Safety check: Ensure the server list is valid and iterable
- if (!Array.isArray(data.data)) {
- console.error('[DEBUG] Invalid server list received. Waiting 1 second before retrying...');
- await delay(1000); // Wait 1 second before retrying
- continue; // Skip the rest of the loop and retry
- }
-
- // Filter and process servers
- for (const server of data.data) {
- if (server.playing === maxPlayers) {
- await rbx_card(server.id, server.playerTokens, server.maxPlayers, server.playing, gameId);
- serversFound++;
-
- if (serversFound >= 16) {
- break;
- }
- } else if (!isCloserToOne && server.playing > maxPlayers) {
- topDownServers.push(server); // Add to top-down fallback list
- } else if (isCloserToOne && server.playing < maxPlayers) {
- bottomUpServers.push(server); // Add to bottom-up fallback list
- }
- }
-
- // Exit if no more servers are available
- if (!data.nextPageCursor) {
- break;
- }
-
- cursor = data.nextPageCursor;
-
- // Adjust delay dynamically
- if (currentDelay > 150) {
- currentDelay = Math.max(150, currentDelay / 2); // Gradually reduce delay
- }
- console.log(`[DEBUG] Waiting ${currentDelay / 1000} seconds before next request...`);
- await delay(currentDelay);
- }
-
- // If no exact matches were found or time limit reached, use fallback servers
- if (serversFound === 0 && (topDownServers.length > 0 || bottomUpServers.length > 0)) {
- // Sort top-down servers by player count (ascending)
- topDownServers.sort((a, b) => a.playing - b.playing);
-
- // Sort bottom-up servers by player count (descending)
- bottomUpServers.sort((a, b) => b.playing - a.playing);
-
- // Combine both fallback lists (prioritize top-down servers first)
- const combinedFallback = [...topDownServers, ...bottomUpServers];
-
- for (const server of combinedFallback) {
- await rbx_card(server.id, server.playerTokens, server.maxPlayers, server.playing, gameId);
- serversFound++;
-
- if (serversFound >= 16) {
- break;
- }
- }
- }
-
- if (serversFound <= 0) {
- notifications('No Servers Found Within The Provided Criteria', 'info', '🔎');
- }
- } catch (error) {
- console.error('[DEBUG] Error in filterServersByPlayerCount:', error);
- } finally {
- Loadingbar(false);
- disableFilterButton(false);
- }
- }
-
- /*********************************************************************************************************************************************************************************************************************************************
- Functions for the 4th button
-
- *********************************************************************************************************************************************************************************************************************************************/
-
- /*******************************************************
- name of function: random_servers
- description: Fetches servers from two different URLs, combines the results, ensures no duplicates, shuffles the list, and passes the server information to the rbx_card function in a random order. Handles 429 errors with retries.
- *******************************************************/
- async function random_servers() {
- // Disable the "Load More" button and show the loading bar
- Loadingbar(true);
- disableFilterButton(true);
- disableLoadMoreButton();
-
- // Get the game ID from the URL
- const gameId = window.location.pathname.split('/')[2];
-
- try {
- // Fetch servers from the first URL with retry logic
- const firstUrl = `https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=10`;
- const firstData = await fetchWithRetry(firstUrl, 3); // Retry up to 3 times
-
- // Wait for 5 seconds
- await delay(5000);
-
- // Fetch servers from the second URL with retry logic
- const secondUrl = `https://games.roblox.com/v1/games/${gameId}/servers/public?sortOrder=1&excludeFullGames=true&limit=10`;
- const secondData = await fetchWithRetry(secondUrl, 3); // Retry up to 3 times
-
- // Combine the servers from both URLs
- const combinedServers = [...firstData.data, ...secondData.data];
-
- // Remove duplicates by server ID
- const uniqueServers = [];
- const seenServerIds = new Set();
-
- for (const server of combinedServers) {
- if (!seenServerIds.has(server.id)) {
- seenServerIds.add(server.id);
- uniqueServers.push(server);
- }
- }
-
- // Shuffle the unique servers array
- const shuffledServers = shuffleArray(uniqueServers);
-
- // Get the first 16 shuffled servers
- const selectedServers = shuffledServers.slice(0, 16);
-
- // Process each server in random order
- for (const server of selectedServers) {
- const {
- id: serverId,
- playerTokens,
- maxPlayers,
- playing
- } = server;
-
- // Pass the server data to the card creation function
- await rbx_card(serverId, playerTokens, maxPlayers, playing, gameId);
- }
- } catch (error) {
- console.error('Error fetching server data:', error);
- notifications('Error: Failed to fetch server data. Please try again later.', 'error', '⚠️');
- } finally {
- // Hide the loading bar and enable the filter button
- Loadingbar(false);
- disableFilterButton(false);
- }
- }
-
- /*******************************************************
- name of function: fetchWithRetry
- description: Fetches data from a URL with retry logic for 429 errors. this is for this unique function
- *******************************************************/
- async function fetchWithRetry(url, retries) {
- for (let i = 0; i < retries; i++) {
- try {
- const response = await fetch(url);
- if (response.status === 429) {
- // If 429 error, wait 10 seconds and retry
- console.log(`Rate limited. Retrying in 10 seconds... (Attempt ${i + 1}/${retries})`);
- await delay(10000); // Wait 10 seconds
- continue;
- }
- if (!response.ok) {
- throw new Error(`HTTP error: ${response.status}`);
- }
- return await response.json();
- } catch (error) {
- if (i === retries - 1) {
- // If no retries left, throw the error
- throw error;
- }
- }
- }
- }
-
- /*******************************************************
- name of function: shuffleArray
- description: Shuffles an array using the Fisher-Yates algorithm.
- *******************************************************/
- function shuffleArray(array) {
- for (let i = array.length - 1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i + 1)); // Random index from 0 to i
- [array[i], array[j]] = [array[j], array[i]]; // Swap elements
- }
- return array;
- }
-
-
- /*********************************************************************************************************************************************************************************************************************************************
- Functions for the 5th button. taken from my other project
-
- *********************************************************************************************************************************************************************************************************************************************/
-
- // so we inject css into the page. if ur on light mode some stuff may look weird so not my fault
- const style = document.createElement('style');
- style.textContent = `
- /* Overlay for the stupid thingy black screen*/
- .overlay {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.7); /* Dark semi-transparent background */
- z-index: 1000; /* Ensure overlay is below the popup */
- }
-
- /* Popup Container for the server region*/
- .filter-popup {
- background-color: #2d2d2d; /* Dark background */
- color: #ffffff; /* White text */
- padding: 20px;
- border-radius: 10px;
- box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
- width: 300px;
- max-width: 90%;
- position: fixed; /* Fixed positioning */
- top: 50%; /* Center vertically */
- left: 50%; /* Center horizontally */
- transform: translate(-50%, -50%); /* Offset to truly center */
- text-align: center;
- z-index: 1001; /* Ensure popup is above the overlay */
- }
-
- /* Close Button for the server selector*/
- #closePopup {
- position: absolute;
- top: 10px;
- right: 10px;
- background: #ff4444; /* Red background */
- border: none;
- color: white;
- font-size: 16px;
- cursor: pointer;
- width: 24px;
- height: 24px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- #closePopup:hover {
- background: #cc0000; /* Darker red on hover */
- }
-
- /* Label */
- .filter-popup label {
- display: block;
- margin-bottom: 10px;
- font-size: 16px;
- color: #ffffff;
- }
-
- /* Dropdown */
- .filter-popup select {
- background-color: #444; /* Dark gray background */
- color: #ffffff; /* White text */
- padding: 8px;
- border-radius: 5px;
- border: 1px solid #666; /* Gray border */
- width: 100%;
- margin-bottom: 10px;
- font-size: 14px;
- }
-
- .filter-popup select:focus {
- border-color: #888; /* Lighter border on focus */
- outline: none;
- }
-
- /* Custom Input */
- .filter-popup input[type="number"] {
- background-color: #444; /* Dark gray background */
- color: #ffffff; /* White text */
- padding: 8px;
- border-radius: 5px;
- border: 1px solid #666; /* Gray border */
- width: 100%;
- margin-bottom: 10px;
- font-size: 14px;
- }
-
- .filter-popup input[type="number"]:focus {
- border-color: #888; /* Lighter border on focus */
- outline: none;
- }
-
- /* Confirm Button */
- #confirmServerCount {
- background-color: #444; /* Dark gray background */
- color: #ffffff; /* White text */
- padding: 8px 16px;
- border: 1px solid #666; /* Gray border */
- border-radius: 5px;
- cursor: pointer;
- font-size: 14px;
- width: 100%;
- transition: background-color 0.3s ease;
- }
-
- #confirmServerCount:hover {
- background-color: #666; /* Lighter gray on hover */
- }
- .rbx-game-server-item.highlighted {
- border: 2px solid green;
- border-radius: 8px;
- }
- .fetch-button:disabled {
- opacity: 0.5;
- cursor: not-allowed;
- }
- `;
- document.head.appendChild(style);
-
-
- // Function to show the message under the "Load More" button
- function showMessage(message) {
- const loadMoreButtonContainer = document.querySelector('.rbx-running-games-footer');
-
- if (!loadMoreButtonContainer) {
- console.error("Load More button container not found!");
- return;
- }
-
- // Create the message element
- const messageElement = document.createElement('div');
- messageElement.className = 'filter-message';
- messageElement.textContent = message;
-
- // Clear any existing message and append the new one
- const existingMessage = loadMoreButtonContainer.querySelector('.filter-message');
- if (existingMessage) {
- existingMessage.remove(); // Remove the existing message if it exists
- }
-
- loadMoreButtonContainer.appendChild(messageElement);
-
- return messageElement;
- }
-
- // Function to hide the message of the showmessage functioon
- function hideMessage() {
- const messageElement = document.querySelector('.filter-message');
- if (messageElement) messageElement.remove();
- }
-
- // Function to show the popup for random stuff
- function showPopup() {
- const overlay = document.createElement('div');
- overlay.className = 'overlay';
-
- const popup = document.createElement('div');
- popup.className = 'filter-popup';
- popup.textContent = 'Filtering servers, please wait...';
-
- document.body.appendChild(overlay);
- document.body.appendChild(popup);
-
- return popup;
- }
-
- // Function to hide the popup for the stuff
- function hidePopup() {
- const popup = document.querySelector('.filter-popup');
- const overlay = document.querySelector('.overlay');
-
- if (popup) popup.remove();
- if (overlay) overlay.remove();
- }
-
- // Function to fetch server details so game id and job id. yea!
- async function fetchServerDetails(gameId, jobId) {
- return new Promise((resolve, reject) => {
- GM_xmlhttpRequest({
- method: "POST",
- url: "https://gamejoin.roblox.com/v1/join-game-instance", // url for game id
- headers: { // doesent need cookie cuase of magic
- "Content-Type": "application/json",
- "User-Agent": "Roblox/WinInet",
- },
- data: JSON.stringify({
- placeId: gameId,
- gameId: jobId
- }),
- onload: function(response) {
- const json = JSON.parse(response.responseText);
-
- console.log("API Response:", json); // This prints the full response
-
- // Check if the response indicates that the user needs to purchase the game
- if (json.status === 12 && json.message === 'You need to purchase access to this game before you can play.') { // yea error message!
- reject('purchase_required'); // Special error code for this case yea!
- return;
- }
-
- const address = json?.joinScript?.UdmuxEndpoints?.[0]?.Address ?? json?.joinScript?.MachineAddress;
-
- if (!address) {
- console.error("API Response (Unknown Location) Which means Full Server!:", json); // Log the API response for debug
- reject(`Unable to fetch server location: Status ${json.status}`); // debug
- return;
- }
-
- const location = serverRegionsByIp[address.replace(/^(128\.116\.\d+)\.\d+$/, "$1.0")]; // lmao all servers atart with this so yea dont argue with me
-
- if (!location) {
- console.error("API Response (Unknown Location):", json); // Log the API response into the chat. might remove it from production but idc rn
- reject(`Unknown server address ${address}`);
- return;
- }
-
- resolve(location);
- },
- onerror: function(error) {
- console.error("API Request Failed:", error); // damn if this happpens idk what to tell u
- reject(`Failed to fetch server details: ${error}`);
- },
- });
- });
- }
-
- // cusomt delay also known as sleep fucntion in js cause this language sucks and doesent have a default function
- function delay(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
-
- // Function to create a popup for selecting the number of servers
- // basically yea thats what it doesent
- function createServerCountPopup(callback) {
- const overlay = document.createElement('div');
- overlay.className = 'overlay';
-
- const popup = document.createElement('div');
- popup.className = 'filter-popup'; // reason 100 is selected because thjats how many the api will show per request
- popup.innerHTML = `
- <button id="closePopup">X</button>
- <label for="serverCount">Enter the number of servers to search (higher values yield more location variety).</label>
- <select id="serverCount">
- <option value="10">10 Servers</option>
- <option value="25">25 Servers</option>
- <option value="50">50 Servers</option>
- <option value="100" selected>100 Servers</option>
- <option value="200">200 Servers</option>
- <option value="500">500 Servers</option>
- <option value="1000">1000 Servers</option>
- <option value="custom">Custom</option>
- </select>
- <input id="customServerCount" type="number" min="1" max="1000" placeholder="Enter a number (1-1000)" style="display: none;">
- <button id="confirmServerCount">Confirm</button>
- `;
-
- document.body.appendChild(overlay);
- document.body.appendChild(popup);
-
- const serverCountDropdown = popup.querySelector('#serverCount');
- const customServerCountInput = popup.querySelector('#customServerCount');
- const confirmButton = popup.querySelector('#confirmServerCount');
- const closeButton = popup.querySelector('#closePopup');
-
- // Show/hide custom input based on dropdown selection
- serverCountDropdown.addEventListener('change', () => {
- if (serverCountDropdown.value === 'custom') {
- customServerCountInput.style.display = 'block';
- } else {
- customServerCountInput.style.display = 'none';
- }
- });
-
- // button click on start or what ever
- confirmButton.addEventListener('click', () => {
- let serverCount;
-
- if (serverCountDropdown.value === 'custom') {
- serverCount = parseInt(customServerCountInput.value);
-
- // Validate custom input
- if (isNaN(serverCount) || serverCount < 1 || serverCount > 1000) {
- notifications('Error: Please enter a valid number between 1 and 1000.', 'error', '⚠️')
- return;
- }
- } else {
- serverCount = parseInt(serverCountDropdown.value);
- }
-
- // Show an alert if the user selects a number above 100
- if (serverCount > 100) { // error cause people dont know about this maybe. idk yea so here. also if u think this is a stupid way i should have done it before the button press idc so yea
- notifications('Warning: Searching over 100 servers may take some time and you might get rate limited!', 'warning', '❗');
- }
-
- // Pass the selected server count to the callback
- callback(serverCount);
- disableFilterButton(true); // disbale filter button
- disableLoadMoreButton(true); // disable load more button
- notifications('Note: Filter Button is disabled as this function is resource intensive. \nRefresh the page to call other functions/press other buttons.', 'info', '⚠️')
- hidePopup();
- Loadingbar(true); // enable loading bar
- });
-
- // Close button logic :))
- closeButton.addEventListener('click', () => {
- hidePopup();
- });
-
- // Function to hide the popup
- // yea im dumb and used the same function name but it works and im too lazy to change it
- function hidePopup() {
- document.body.removeChild(overlay);
- document.body.removeChild(popup);
- }
- }
-
- // Function to fetch public servers
- // totallimit is amount of sevrers to fetch
- async function fetchPublicServers(gameId, totalLimit) {
- let servers = [];
- let cursor = null;
-
- while (servers.length < totalLimit) { // too lazy to comment any of this. hopefully i remember what this does in the future
- const url = `https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=100${cursor ? `&cursor=${cursor}` : ''}`;
-
- const response = await new Promise((resolve, reject) => {
- GM_xmlhttpRequest({
- method: "GET",
- url: url,
- onload: function(response) {
- resolve(JSON.parse(response.responseText));
- },
- onerror: function(error) {
- reject(`Failed to fetch public servers: ${error}`);
- },
- });
- });
-
- servers = servers.concat(response.data);
-
- if (!response.nextPageCursor || servers.length >= totalLimit) {
- break;
- }
-
- cursor = response.nextPageCursor;
- await delay(3000); // wait 3 seconds before each page request. if u think this is slow i tried 1 second i got rate limited :|
- }
-
- return servers.slice(0, totalLimit);
- }
-
- // Function to create dropdown menus for filtering
- function createFilterDropdowns(servers) {
- const filterContainer = document.createElement('div');
- filterContainer.className = 'filter-container';
-
- const countryDropdown = document.createElement('select');
- countryDropdown.id = 'countryFilter';
- countryDropdown.innerHTML = '<option value="">All Countries</option>';
- countryDropdown.style.backgroundColor = '#333'; // Dark gray background
- countryDropdown.style.color = '#fff'; // White text
- countryDropdown.style.borderRadius = '8px'; // Rounded corners
- countryDropdown.style.padding = '8px'; // Increase size
- countryDropdown.style.fontSize = '16px'; // Increase font size
- countryDropdown.style.border = 'none'; // Remove default border
-
- const cityDropdown = document.createElement('select');
- cityDropdown.id = 'cityFilter';
- cityDropdown.innerHTML = '<option value="">All Cities</option>';
- cityDropdown.style.backgroundColor = '#333'; // Dark gray background
- cityDropdown.style.color = '#fff'; // White text
- cityDropdown.style.borderRadius = '8px'; // Rounded corners
- cityDropdown.style.padding = '8px'; // Increase size hehehehe
- cityDropdown.style.fontSize = '16px'; // Increase font size
- cityDropdown.style.border = 'none'; // Remove default border
- cityDropdown.style.marginLeft = '5px'; // move right cause im too lazy to fix
-
- // Count the number of servers per country and add them to the dropdown
- const countryCounts = {};
- servers.forEach(server => {
- const country = server.location.country.name;
- countryCounts[country] = (countryCounts[country] || 0) + 1;
- });
-
- // Populate country dropdown with server counts
- Object.keys(countryCounts).forEach(country => {
- const option = document.createElement('option');
- option.value = country;
- option.textContent = `${country} (${countryCounts[country]})`;
- countryDropdown.appendChild(option);
- });
-
- // add the city dropdown based on selected country
- countryDropdown.addEventListener('change', () => {
- const selectedCountry = countryDropdown.value;
- cityDropdown.innerHTML = '<option value="">All Cities</option>';
-
- if (selectedCountry) {
- // Count the number of servers per city in the selected country
- const cityCounts = {};
- servers
- .filter(server => server.location.country.name === selectedCountry)
- .forEach(server => {
- const city = server.location.city;
- const region = server.location.region?.name;
- const cityKey = region ? `${city}, ${region}` : city;
- cityCounts[cityKey] = (cityCounts[cityKey] || 0) + 1;
- });
-
- // Populate city dropdown with server counts
- Object.keys(cityCounts).forEach(city => {
- const option = document.createElement('option');
- option.value = city;
- option.textContent = `${city} (${cityCounts[city]})`;
- cityDropdown.appendChild(option);
- });
-
- // Auto-select the city if there's only one make users life easier
- // wow ik i made the users life easier for once thats crazy!!! :OOOOO
- const cities = Object.keys(cityCounts);
- if (cities.length === 1) {
- cityDropdown.value = cities[0];
- // displayFilteredServers(selectedCountry, cities[0]); // if this breaks something which it doesent seem like it i will enable it later
- }
- }
- });
-
- filterContainer.appendChild(countryDropdown);
- filterContainer.appendChild(cityDropdown);
-
- return filterContainer;
- }
-
- // Function to filter servers based on selected country and city cause im lazy
- function filterServers(servers, country, city) {
- return servers.filter(server => {
- const matchesCountry = !country || server.location.country.name === country;
- const matchesCity = !city || `${server.location.city}${server.location.region?.name ? `, ${server.location.region.name}` : ''}` === city;
- return matchesCountry && matchesCity;
- });
- }
-
- // Function to sort servers by ping. maybe inaccurate but thats roblox's problem not mine
- function sortServersByPing(servers) {
- return servers.sort((a, b) => a.server.ping - b.server.ping);
- }
-
- async function fetchPlayerThumbnails_servers(playerTokens) {
- const body = playerTokens.map(token => ({
- requestId: `0:${token}:AvatarHeadshot:150x150:png:regular`,
- type: "AvatarHeadShot",
- targetId: 0,
- token,
- format: "png",
- size: "150x150",
- }));
-
- const response = await fetch("https://thumbnails.roblox.com/v1/batch", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
- return data.data || [];
- }
-
- async function rebuildServerList(gameId, totalLimit, best_connection) {
- const serverListContainer = document.getElementById("rbx-game-server-item-container");
-
-
- // If "Best Connection" is enabled
- // FUNCTION FOR THE 6TH BUTTON!
- if (best_connection === true) {
- // Ask for the user's location
- const userLocation = await getUserLocation();
- if (!userLocation) {
- //notifications('Error: Unable to fetch your location. Please enable location access.', 'error', '⚠️');
- return;
- }
-
- // Fetch 50 servers
- const servers = await fetchPublicServers(gameId, 50);
- if (servers.length === 0) {
- notifications('Error: No servers found. Please try again later.', 'error', '⚠️');
- return;
- }
-
- // Calculate distances and find the closest server
- let closestServer = null;
- let minDistance = Infinity;
- let closestServerLocation = null;
-
- for (const server of servers) {
- const {
- id: serverId,
- maxPlayers,
- playing
- } = server;
-
- // Skip full servers
- if (playing >= maxPlayers) {
- continue;
- }
-
- try {
- // Fetch server location
- const location = await fetchServerDetails(gameId, serverId);
-
- // Calculate distance
- const distance = calculateDistance(
- userLocation.latitude,
- userLocation.longitude,
- location.latitude,
- location.longitude
- );
-
- // Update closest server
- if (distance < minDistance) {
- minDistance = distance;
- closestServer = server;
- closestServerLocation = location;
- }
- } catch (error) {
- console.error(`Error fetching details for server ${serverId}:`, error);
- // Skip this server and continue with the next one
- continue;
- }
- }
-
- if (closestServer) {
- // Automatically join the closest server
- Roblox.GameLauncher.joinGameInstance(gameId, closestServer.id);
- notifications(`Joining nearest server!
- Server ID: ${closestServer.id}
- Distance: ${(minDistance / 1.609).toFixed(2)} miles | ${minDistance.toFixed(2)} km
- Location (Country): ${closestServerLocation.country.name}.`, 'success', '🚀');
-
- disableFilterButton(false);
- Loadingbar(false);
- } else {
- notifications('No valid servers found. Please try again later after refreshing the webpage.', 'error', '⚠️');
- }
-
- return; // Exit the function after joining the best server
- }
-
- // Rest of the original function (for non-"Best Connection" mode)
- if (!serverListContainer) {
- console.error("Server list container not found!");
- const popup = showPopup();
- notifications('Error: No Servers found. There is nobody playing this game. :(', 'warning', '❗');
- return;
- }
-
- const messageElement = showMessage("Filtering servers, please wait...");
-
- try {
- const servers = await fetchPublicServers(gameId, totalLimit);
- const totalServers = servers.length;
- let skippedServers = 0;
-
- messageElement.textContent = `Filtering servers, please do not leave this page as it slows down the search...\n${totalServers} servers found, 0 servers loaded.`;
- notifications(`Please do not leave this page as it slows down the search. \nFound a total of ${totalServers} servers found.`, 'success', '👍');
-
- const serverDetails = [];
- for (let i = 0; i < servers.length; i++) {
- const server = servers[i];
- const {
- id: serverId,
- maxPlayers,
- playing,
- ping,
- fps,
- playerTokens
- } = server;
-
- let location;
- try {
- location = await fetchServerDetails(gameId, serverId);
- } catch (error) {
- if (error === 'purchase_required') {
- messageElement.textContent = "Cannot access server data because you haven't purchased the game.";
- notifications('Error: Cannot access server data because you haven\'t purchased the game.', 'error', '⚠️');
- Loadingbar(false); // disable loading bar
- return;
- } else {
- console.error(error);
- location = {
- city: "Unknown",
- country: {
- name: "Unknown",
- code: "??"
- }
- };
- }
- }
-
- if (location.city === "Unknown" || playing >= maxPlayers) {
- console.log(`Skipping server ${serverId} because it is full or location is unknown.`);
- skippedServers++;
- continue;
- }
-
- // Fetch player thumbnails
- const playerThumbnails = playerTokens && playerTokens.length > 0 ? await fetchPlayerThumbnails_servers(playerTokens) : [];
-
- serverDetails.push({
- server,
- location,
- playerThumbnails
- });
-
- messageElement.textContent = `Filtering servers, please do not leave this page...\n${totalServers} servers found, ${i + 1} server locations found`;
- }
-
- if (serverDetails.length === 0) {
- messageElement.textContent = "No servers found. Please try again with an increase in the number of servers to search for.";
- notifications('Error: No servers found. Please try again with an increase in the number of servers to search for.', 'error', '⚠️');
- Loadingbar(false); // disable loading bar
- return;
- }
-
- const loadedServers = totalServers - skippedServers;
- notifications(`Filtering complete!\n${totalServers} servers found, ${loadedServers} servers loaded, ${skippedServers} servers skipped (full).`, 'success', '👍');
- messageElement.textContent = `Filtering complete!\n${totalServers} servers found, ${loadedServers} servers loaded, ${skippedServers} servers skipped (full).`;
- Loadingbar(false); // disable loading bar
-
- // Add filter dropdowns
- const filterContainer = createFilterDropdowns(serverDetails);
- serverListContainer.parentNode.insertBefore(filterContainer, serverListContainer);
-
- // Style the server list container to use a grid layout
- serverListContainer.style.display = "grid";
- serverListContainer.style.gridTemplateColumns = "repeat(4, 1fr)"; // 4 columns
- serverListContainer.style.gap = "16px"; // Gap between cards
-
- const displayFilteredServers = (country, city) => {
- serverListContainer.innerHTML = "";
-
- const filteredServers = filterServers(serverDetails, country, city);
- const sortedServers = sortServersByPing(filteredServers);
-
- sortedServers.forEach(({
- server,
- location,
- playerThumbnails
- }) => {
- const serverCard = document.createElement("li");
- serverCard.className = "rbx-game-server-item col-md-3 col-sm-4 col-xs-6";
-
- // Set consistent width and height for the server card
- serverCard.style.width = "100%"; // Take up full width of the grid cell
- serverCard.style.minHeight = "400px"; // Set a minimum height
- serverCard.style.display = "flex";
- serverCard.style.flexDirection = "column";
- serverCard.style.justifyContent = "space-between";
- serverCard.style.boxSizing = "border-box"; // Include padding and border in dimensions
-
- // Remove any conflicting outline (e.g., from .highlighted class)
- serverCard.style.outline = 'none';
-
- // Determine the group and set the outline color
- let outlineColor;
- if (server.ping < 100) {
- outlineColor = 'green'; // Best ping
- } else if (server.ping < 200) {
- outlineColor = 'orange'; // Medium ping
- } else {
- outlineColor = 'red'; // Bad ping
- }
-
- // Apply the new outline and outlineOffset
- serverCard.style.outline = `3px solid ${outlineColor}`;
- serverCard.style.outlineOffset = '-6px';
- serverCard.style.padding = '6px';
- serverCard.style.borderRadius = '8px';
-
- // Create a container for player thumbnails
- const thumbnailsContainer = document.createElement("div");
- thumbnailsContainer.className = "player-thumbnails-container";
- thumbnailsContainer.style.display = "grid";
- thumbnailsContainer.style.gridTemplateColumns = "repeat(3, 60px)"; // 3 columns
- thumbnailsContainer.style.gridTemplateRows = "repeat(2, 60px)"; // 2 rows
- thumbnailsContainer.style.gap = "5px";
- thumbnailsContainer.style.marginBottom = "10px";
-
- // Add player thumbnails to the container (max 5)
- const maxThumbnails = 5;
- const displayedThumbnails = playerThumbnails.slice(0, maxThumbnails);
- displayedThumbnails.forEach(thumb => {
- if (thumb && thumb.imageUrl) {
- const img = document.createElement("img");
- img.src = thumb.imageUrl;
- img.className = "avatar-card-image";
- img.style.width = "60px";
- img.style.height = "60px";
- img.style.borderRadius = "50%";
- thumbnailsContainer.appendChild(img);
- }
- });
-
- // Add a placeholder for hidden players
- const hiddenPlayers = server.playing - displayedThumbnails.length;
- if (hiddenPlayers > 0) {
- const placeholder = document.createElement("div");
- placeholder.className = "avatar-card-image";
- placeholder.style.width = "60px";
- placeholder.style.height = "60px";
- placeholder.style.borderRadius = "50%";
- placeholder.style.backgroundColor = "#BDBEBE80"; // Dark gray background
- placeholder.style.display = "flex";
- placeholder.style.alignItems = "center";
- placeholder.style.justifyContent = "center";
- placeholder.style.color = "#fff"; // White text
- placeholder.style.fontSize = "14px";
- placeholder.textContent = `+${hiddenPlayers}`;
- thumbnailsContainer.appendChild(placeholder);
- }
-
- // Server card content
- const cardItem = document.createElement("div");
- cardItem.className = "card-item";
- cardItem.style.display = "flex";
- cardItem.style.flexDirection = "column";
- cardItem.style.justifyContent = "space-between";
- cardItem.style.height = "100%"; // Ensure the card content takes up the full height
-
- cardItem.innerHTML = `
- <!-- Player thumbnails at the top -->
- ${thumbnailsContainer.outerHTML}
- <div class="rbx-game-server-details game-server-details">
- <div class="text-info rbx-game-status rbx-game-server-status text-overflow">
- ${server.playing} of ${server.maxPlayers} people max
- </div>
- <div class="server-player-count-gauge border">
- <div class="gauge-inner-bar border" style="width: ${(server.playing / server.maxPlayers) * 100}%;"></div>
- </div>
- <span data-placeid="${gameId}">
- <button type="button" class="btn-full-width btn-control-xs rbx-game-server-join game-server-join-btn btn-primary-md btn-min-width">Join</button>
- </span>
- </div>
- <!-- Generated info (ping, location, FPS) at the bottom -->
- <div style="margin-top: 10px; text-align: center;">
- <div class="ping-info">Ping: ${server.ping}ms</div>
- <div class="location-info">${location.city}, ${location.country.name}</div>
- <div class="fps-info">FPS: ${Math.round(server.fps)}</div>
- </div>
- `;
-
- const joinButton = cardItem.querySelector(".rbx-game-server-join");
- joinButton.addEventListener("click", () => {
- console.log(`Roblox.GameLauncher.joinGameInstance(${gameId}, "${server.id}")`);
- Roblox.GameLauncher.joinGameInstance(gameId, server.id); // join server
- });
-
- const container = adjustJoinButtonContainer(joinButton);
- const inviteButton = createInviteButton(gameId, server.id);
- container.appendChild(inviteButton);
-
- serverCard.appendChild(cardItem);
- serverListContainer.appendChild(serverCard);
- });
- };
-
- // Add event listeners to dropdowns
- const countryFilter = document.getElementById('countryFilter');
- const cityFilter = document.getElementById('cityFilter');
-
- countryFilter.addEventListener('change', () => {
- displayFilteredServers(countryFilter.value, cityFilter.value);
- });
-
- cityFilter.addEventListener('change', () => {
- displayFilteredServers(countryFilter.value, cityFilter.value);
- });
-
- // Display all servers initially
- displayFilteredServers("", "");
-
- setTimeout(() => {
- hideMessage();
- }, 3000);
- } catch (error) {
- console.error("Error rebuilding server list:", error);
- notifications('An error occurred while filtering servers. Please try again.', 'error', '😔');
- messageElement.textContent = "An error occurred while filtering servers. Please try again.";
- Loadingbar(false); // enable loading bar
- } finally {
- Loadingbar(false); // omg bruh i just realzed i could put this here but now im too lazy to thorugh the code to remove all of the loading bar disabl functions
- }
- }
-
- // Function to extract the game ID from the URL
- function extractGameId() {
- const url = window.location.href;
- const match = url.match(/roblox\.com\/games\/(\d+)/);
-
- if (match && match[1]) {
- return match[1]; // Return the game ID
- }
- return null; // Return null if no game ID is found
- }
-
- // Log the game ID to the console
- const gameId = extractGameId();
-
- // Function to create and append the Invite button
- function createInviteButton(placeId, serverId) { // too lazy to comment this function tbh just ready the name
- const inviteButton = document.createElement('button');
- inviteButton.textContent = 'Invite';
- inviteButton.className = 'btn-control-xs btn-primary-md btn-min-width btn-full-width';
- inviteButton.style.width = '25%';
- inviteButton.style.marginLeft = '5px';
-
- inviteButton.style.padding = '4px 8px';
- inviteButton.style.fontSize = '12px';
- inviteButton.style.borderRadius = '8px';
- inviteButton.style.backgroundColor = '#393b3d';
- inviteButton.style.borderColor = '#bdbebe';
- inviteButton.style.color = '#bdbebe';
- inviteButton.style.cursor = 'pointer';
- inviteButton.style.fontWeight = '500';
- inviteButton.style.textAlign = 'center';
- inviteButton.style.whiteSpace = 'nowrap';
- inviteButton.style.verticalAlign = 'middle';
- inviteButton.style.lineHeight = '100%';
- inviteButton.style.fontFamily = 'Builder Sans, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif';
- inviteButton.style.textRendering = 'auto';
- inviteButton.style.webkitFontSmoothing = 'antialiased';
- inviteButton.style.mozOsxFontSmoothing = 'grayscale';
-
- inviteButton.addEventListener('mouseenter', () => {
- inviteButton.style.color = '#ffffff';
- inviteButton.style.borderColor = '#ffffff';
- });
- inviteButton.addEventListener('mouseleave', () => {
- inviteButton.style.color = '#bdbebe';
- inviteButton.style.borderColor = '#bdbebe';
- });
-
- inviteButton.addEventListener('click', () => {
- const inviteLink = `https://oqarshi.github.io/Invite/?placeid=${placeId}&serverid=${serverId}`;
- navigator.clipboard.writeText(inviteLink).then(() => {
- console.log(`Invite link copied to clipboard: ${inviteLink}`);
- notifications('Success! Invite link copied to clipboard!', 'success', '🎉');
- }).catch(() => {
- console.error('Failed to copy invite link.');
- notifications('Error: Failed to copy invite link', 'error', '😔');
- });
- });
-
- return inviteButton;
- }
-
- // Function to adjust the Join button and its container
- function adjustJoinButtonContainer(joinButton) {
- const container = document.createElement('div');
- container.style.display = 'flex';
- container.style.width = '100%';
-
- joinButton.style.width = '75%';
-
- joinButton.parentNode.insertBefore(container, joinButton);
- container.appendChild(joinButton);
-
- return container;
- }
-
-
-
-
- /*********************************************************************************************************************************************************************************************************************************************
- Functions for the 6th button.
-
- *********************************************************************************************************************************************************************************************************************************************/
-
-
-
-
- function calculateDistance(lat1, lon1, lat2, lon2) {
- const R = 6371; // Radius of the Earth in kilometers
- const dLat = (lat2 - lat1) * (Math.PI / 180);
- const dLon = (lon2 - lon1) * (Math.PI / 180);
- const a =
- Math.sin(dLat / 2) * Math.sin(dLat / 2) +
- Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) *
- Math.sin(dLon / 2) * Math.sin(dLon / 2);
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- return R * c; // Distance in kilometers
- }
-
-
- function getUserLocation() {
- return new Promise((resolve, reject) => {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(
- (position) => {
- notifications('We successfully detected your location.\nConnecting you to a nearby server in about 15-20 seconds. 😊', 'success', '🌎');
- disableLoadMoreButton(true);
- disableFilterButton(true);
- Loadingbar(true);
- resolve({
- latitude: position.coords.latitude,
- longitude: position.coords.longitude,
- });
- },
- (error) => {
- console.error('Error getting user location:', error);
- disableLoadMoreButton(true);
- disableFilterButton(true);
- Loadingbar(true);
- notifications('Error getting user location.\nPlease enable location permissions for this website.\nAssuming your location is New York in the United States.', 'error', '⚠️');
- // Fallback to a default location (e.g., New York City)
- resolve({
- latitude: 40.7128, // Default latitude (New York City)
- longitude: -74.0060, // Default longitude (New York City)
- });
- }
- );
- } else {
- console.error('Geolocation is not supported by this browser.');
- disableLoadMoreButton(true);
- disableFilterButton(true);
- Loadingbar(true);
- notifications('Error getting user location.\nThis browser doesent support location.\nAssuming your location is New York in the United States.', 'error', '⚠️');
- // Fallback to a default location (e.g., New York City)
- resolve({
- latitude: 40.7128, // Default latitude (New York City)
- longitude: -74.0060, // Default longitude (New York City)
- });
- }
- });
- }
-
-
-
- /*********************************************************************************************************************************************************************************************************************************************
- Functions for the 7th button.
-
- *********************************************************************************************************************************************************************************************************************************************/
- async function auto_join_small_server() {
- // Disable the "Load More" button and show the loading bar
- Loadingbar(true);
- disableFilterButton(true);
- disableLoadMoreButton();
-
- // Get the game ID from the URL
- const gameId = window.location.pathname.split('/')[2];
-
- // Retry mechanism for 429 errors
- let retries = 3; // Number of retries
- let success = false;
-
- while (retries > 0 && !success) {
- try {
- // Fetch server data using GM_xmlhttpRequest
- const data = await new Promise((resolve, reject) => {
- GM_xmlhttpRequest({
- method: "GET",
- url: `https://games.roblox.com/v1/games/${gameId}/servers/public?sortOrder=1&excludeFullGames=true&limit=100`,
- onload: function(response) {
- if (response.status === 429) {
- reject('429: Too Many Requests');
- } else if (response.status >= 200 && response.status < 300) {
- resolve(JSON.parse(response.responseText));
- } else {
- reject(`HTTP error: ${response.status}`);
- }
- },
- onerror: function(error) {
- reject(error);
- },
- });
- });
-
- // Find the server with the lowest player count
- let minPlayers = Infinity;
- let targetServer = null;
-
- for (const server of data.data) {
- if (server.playing < minPlayers) {
- minPlayers = server.playing;
- targetServer = server;
- }
- }
-
- if (targetServer) {
- // Join the server with the lowest player count
- Roblox.GameLauncher.joinGameInstance(gameId, targetServer.id);
- notifications(`Joining a server with ${targetServer.playing} player(s).`, 'success', '🚀');
- success = true; // Mark as successful
- } else {
- notifications('No available servers found.', 'error', '⚠️');
- break; // Exit the loop if no servers are found
- }
- } catch (error) {
- if (error === '429: Too Many Requests' && retries > 0) {
- console.log('Rate limited. Retrying in 10 seconds...');
- notifications('Rate limited. Retrying in 10 seconds...', 'warning', '⏳');
- await delay(10000); // Wait 10 seconds before retrying
- retries--;
- } else {
- console.error('Error fetching server data:', error);
- notifications('Error: Failed to fetch server data. Please try again later.', 'error', '⚠️');
- break; // Exit the loop if it's not a 429 error or no retries left
- }
- }
- }
-
- // Hide the loading bar and enable the filter button
- Loadingbar(false);
- disableFilterButton(false);
- }
-
-
- /*********************************************************************************************************************************************************************************************************************************************
- Functions for the 8th button.
-
- *********************************************************************************************************************************************************************************************************************************************/
-
- function credits() {
- // Inject CSS for the popup
- const css = `
- .credits-popup {
- display: flex;
- position: fixed;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.8);
- justify-content: center;
- align-items: center;
- z-index: 1000;
- opacity: 0;
- animation: fadeIn 0.5s ease-in-out forwards;
- }
- .credits-popup-content {
- background-color: #000;
- padding: 20px;
- border-radius: 10px;
- width: 300px;
- box-shadow: 0 5px 15px rgba(255, 255, 255, 0.1);
- text-align: center;
- position: relative;
- color: #fff;
- }
- .credits-popup-content h2 {
- margin-top: 0;
- color: #fff;
- }
- .credits-popup-content .version {
- font-size: 14px;
- color: #aaa;
- margin-bottom: 10px;
- }
- .credits-popup-content ul {
- list-style-type: none;
- padding: 0;
- }
- .credits-popup-content ul li {
- margin: 10px 0;
- color: #bbb;
- }
- .credits-popup-content a {
- color: #4da6ff;
- text-decoration: none;
- }
- .credits-popup-content a:hover {
- text-decoration: underline;
- }
- .credits-popup-close {
- position: absolute;
- top: 10px;
- right: 10px;
- font-size: 24px;
- font-weight: bold;
- cursor: pointer;
- color: #fff;
- }
- .credits-popup-close:hover {
- color: #ccc;
- }
- @keyframes fadeIn {
- from { opacity: 0; }
- to { opacity: 1; }
- }
- @keyframes fadeOut {
- from { opacity: 1; }
- to { opacity: 0; }
- }
- `;
-
- // Add CSS to the document
- const style = document.createElement('style');
- style.type = 'text/css';
- style.innerHTML = css;
- document.head.appendChild(style);
-
- // Create the popup HTML
- const popupHTML = `
- <div class="credits-popup">
- <div class="credits-popup-content">
- <span class="credits-popup-close">×</span>
- <div class="version">Rolocate: Version 21.3</div>
- <h2>Credits</h2>
- <p>This project was created by:</p>
- <ul>
- <li>Developer: <a href="https://www.roblox.com/users/545334824/profile" target="_blank">Oqarshi</a></li>
- <li>Special Thanks: <a href="https://chromewebstore.google.com/detail/btroblox-making-roblox-be/hbkpclpemjeibhioopcebchdmohaieln" target="_blank">Btroblox Team ❤️</a></li>
- <li>Source Code for Roblox Locate is available at <a href="https://greasyfork.org/en/scripts/522164-roblox-locate" target="_blank">GreasyFork</a></li>
- <li>Source Code for the invite feature is available at <a href="https://github.com/Oqarshi/Invite" target="_blank">Github</a></li>
- </ul>
- </div>
- </div>
- `;
-
- // Add the popup to the document
- const popupContainer = document.createElement('div');
- popupContainer.innerHTML = popupHTML;
- document.body.appendChild(popupContainer);
-
- // Add event listener to close the popup with animation
- const closeButton = document.querySelector('.credits-popup-close');
- const popup = document.querySelector('.credits-popup');
- closeButton.addEventListener('click', () => {
- popup.style.animation = 'fadeOut 0.5s ease-in-out forwards';
- setTimeout(() => {
- popup.remove(); // Remove the popup from the DOM after animation
- }, 500); // Match the duration of the fadeOut animation
- });
- }
-
- /*********************************************************************************************************************************************************************************************************************************************
- End of: This is all the functions for the 8 buttons
-
- *********************************************************************************************************************************************************************************************************************************************/
-
-
-
- /*********************************************************************************************************************************************************************************************************************************************
- The Universal Functions
-
- *********************************************************************************************************************************************************************************************************************************************/
-
-
- /*******************************************************
- name of function: disableLoadMoreButton
- description: Disables the "Load More" button
- *******************************************************/
- function disableLoadMoreButton() {
- const loadMoreButton = document.querySelector('.rbx-running-games-load-more');
- if (loadMoreButton) {
- loadMoreButton.disabled = true;
- loadMoreButton.style.opacity = '0.5'; // Optional: Make the button look disabled
- loadMoreButton.style.cursor = 'not-allowed'; // Optional: Change cursor to indicate disabled state
- loadMoreButton.title = 'Disabled by Roblox Locator'; // Set tooltip text
- } else {
- console.warn('Load More button not found!');
- }
- }
-
-
-
- /*******************************************************
- name of function: Loadingbar
- description: Shows or hides a loading bar
- *******************************************************/
- function Loadingbar(disable) {
- const serverListSection = document.querySelector('#rbx-running-games');
- const serverCardsContainer = document.querySelector('#rbx-game-server-item-container');
-
- if (disable) {
- // Remove server cards and disable the "Load More" button
- serverCardsContainer.innerHTML = '';
-
- // Create and display the loading bar
- const loadingBar = document.createElement('div');
- loadingBar.id = 'loading-bar';
- loadingBar.style.cssText = `
- width: 100%;
- height: 4px;
- background-color: #1E1E1E;
- position: relative;
- overflow: hidden;
- border-radius: 2px;
- margin-top: 10px;
- `;
-
- const loadingBarInner = document.createElement('div');
- loadingBarInner.style.cssText = `
- width: 50%;
- height: 100%;
- background-color: #00A2FF;
- position: absolute;
- animation: loading 1.5s infinite ease-in-out;
- border-radius: 2px;
- `;
-
- // Add animation keyframes
- const styleSheet = document.createElement('style');
- styleSheet.textContent = `
- @keyframes loading {
- 0% { left: -50%; }
- 100% { left: 100%; }
- }
- `;
- document.head.appendChild(styleSheet);
-
- loadingBar.appendChild(loadingBarInner);
- serverListSection.appendChild(loadingBar);
- } else {
- // Remove the loading bar
- const loadingBar = document.querySelector('#loading-bar');
- if (loadingBar) {
- loadingBar.remove();
- }
- }
- }
-
-
-
- /*******************************************************
- name of function: fetchPlayerThumbnails
- description: Fetches player thumbnails for up to 5 players. Skips the batch if an error occurs.
- *******************************************************/
- async function fetchPlayerThumbnails(playerTokens) {
- // Limit to the first 5 player tokens
- const limitedTokens = playerTokens.slice(0, 5);
-
- const body = limitedTokens.map(token => ({
- requestId: `0:${token}:AvatarHeadshot:150x150:png:regular`,
- type: "AvatarHeadShot",
- targetId: 0,
- token,
- format: "png",
- size: "150x150",
- }));
-
- try {
- const response = await fetch("https://thumbnails.roblox.com/v1/batch", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- body: JSON.stringify(body),
- });
-
- // Check if the response is successful
- if (!response.ok) {
- throw new Error(`HTTP error! Status: ${response.status}`);
- }
-
- const data = await response.json();
- return data.data || []; // Return the data or an empty array if no data is present
- } catch (error) {
- console.error('Error fetching player thumbnails:', error);
- return []; // Return an empty array if an error occurs
- }
- }
-
-
- /*******************************************************
- name of function: disableFilterButton
- description: Disables or enables the filter button based on the input.
- *******************************************************/
- function disableFilterButton(disable) {
- const filterButton = document.querySelector('.RL-filter-button');
- const overlayId = 'filter-button-overlay';
-
- if (filterButton) {
- const parent = filterButton.parentElement;
-
- if (disable) {
- // Disable the filter button with an overlay
- filterButton.disabled = true;
- filterButton.style.opacity = '0.5'; // Make the button look disabled
- filterButton.style.cursor = 'not-allowed'; // Change cursor to indicate disabled state
-
- // Create an overlay if it doesn't exist
- let overlay = document.getElementById(overlayId);
- if (!overlay) {
- overlay = document.createElement('div');
- overlay.id = overlayId;
- overlay.style.position = 'absolute';
- overlay.style.top = '0';
- overlay.style.left = '0';
- overlay.style.width = '100%';
- overlay.style.height = '100%';
- overlay.style.backgroundColor = 'transparent'; // Transparent to maintain UI
- overlay.style.zIndex = '9999'; // High z-index to cover the button
- overlay.style.pointerEvents = 'all'; // Block all interactions
- parent.style.position = 'relative'; // Ensure parent is positioned for absolute overlay
- parent.appendChild(overlay);
- }
- } else {
- // Enable the filter button
- filterButton.disabled = false;
- filterButton.style.opacity = '1'; // Restore opacity
- filterButton.style.cursor = 'pointer'; // Restore cursor
-
- // Remove the overlay if it exists
- const overlay = document.getElementById(overlayId);
- if (overlay) {
- overlay.remove();
- }
- }
- } else {
- console.warn('Filter button not found!');
- }
- }
-
-
-
- async function rbx_card(serverId, playerTokens, maxPlayers, playing, gameId) {
- // Fetch player thumbnails (up to 5)
- const thumbnails = await fetchPlayerThumbnails(playerTokens);
-
- // Create the server card container
- const cardItem = document.createElement('li');
- cardItem.className = 'rbx-game-server-item col-md-3 col-sm-4 col-xs-6';
-
- // Create the player thumbnails container
- const playerThumbnailsContainer = document.createElement('div');
- playerThumbnailsContainer.className = 'player-thumbnails-container';
-
- // Add player thumbnails to the container (up to 5)
- thumbnails.forEach(thumbnail => {
- const playerAvatar = document.createElement('span');
- playerAvatar.className = 'avatar avatar-headshot-md player-avatar';
-
- const thumbnailImage = document.createElement('span');
- thumbnailImage.className = 'thumbnail-2d-container avatar-card-image';
-
- const img = document.createElement('img');
- img.src = thumbnail.imageUrl;
- img.alt = '';
- img.title = '';
-
- thumbnailImage.appendChild(img);
- playerAvatar.appendChild(thumbnailImage);
- playerThumbnailsContainer.appendChild(playerAvatar);
- });
-
- // Add the 6th placeholder for remaining players
- if (playing > 5) {
- const remainingPlayers = playing - 5;
- const placeholder = document.createElement('span');
- placeholder.className = 'avatar avatar-headshot-md player-avatar hidden-players-placeholder';
- placeholder.textContent = `+${remainingPlayers}`;
- placeholder.style.cssText = `
- background-color: #7b7c7d; /* Gray background */
- color: white;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 50%; /* Fully round */
- font-size: 16px; /* Larger font size */
- width: 60px; /* Larger width */
- height: 60px; /* Larger height */
- `;
- playerThumbnailsContainer.appendChild(placeholder);
- }
-
- // Create the server details container
- const serverDetails = document.createElement('div');
- serverDetails.className = 'rbx-game-server-details game-server-details';
-
- // Add server status (e.g., "15 of 15 people max")
- const serverStatus = document.createElement('div');
- serverStatus.className = 'text-info rbx-game-status rbx-game-server-status text-overflow';
- serverStatus.textContent = `${playing} of ${maxPlayers} people max`;
- serverDetails.appendChild(serverStatus);
-
- // Add the player count gauge
- const gaugeContainer = document.createElement('div');
- gaugeContainer.className = 'server-player-count-gauge border';
-
- const gaugeInner = document.createElement('div');
- gaugeInner.className = 'gauge-inner-bar border';
- gaugeInner.style.width = `${(playing / maxPlayers) * 100}%`;
-
- gaugeContainer.appendChild(gaugeInner);
- serverDetails.appendChild(gaugeContainer);
-
- // Create a container for the buttons
- const buttonContainer = document.createElement('div');
- buttonContainer.className = 'button-container';
- buttonContainer.style.cssText = `
- display: flex;
- gap: 8px; /* Space between buttons */
- `;
-
- // Add the "Join" button
- const joinButton = document.createElement('button');
- joinButton.type = 'button';
- joinButton.className = 'btn-full-width btn-control-xs rbx-game-server-join game-server-join-btn btn-primary-md btn-min-width';
- joinButton.textContent = 'Join';
-
- // Add click event to join the server
- joinButton.addEventListener('click', () => {
- Roblox.GameLauncher.joinGameInstance(gameId, serverId);
- });
-
- buttonContainer.appendChild(joinButton);
-
- // Add the "Invite" button
- const inviteButton = document.createElement('button');
- inviteButton.type = 'button';
- inviteButton.className = 'btn-full-width btn-control-xs rbx-game-server-invite game-server-invite-btn btn-secondary-md btn-min-width';
- inviteButton.textContent = 'Invite';
-
- // Add click event to log the invite link
- inviteButton.addEventListener('click', () => {
- const inviteLink = `https://oqarshi.github.io/Invite/?placeid=${gameId}&serverid=${serverId}`;
- //console.log('Copied invite link:', inviteLink);
- navigator.clipboard.writeText(inviteLink).then(() => {
- notifications('Success! Invite link copied to clipboard!', 'success', '🎉');
- //console.log('Invite link copied to clipboard');
- }).catch(err => {
- console.error('Failed to copy invite link:', err);
- });
- });
-
- buttonContainer.appendChild(inviteButton);
-
- // Add the button container to the server details
- serverDetails.appendChild(buttonContainer);
-
- // Assemble the card
- const cardContainer = document.createElement('div');
- cardContainer.className = 'card-item';
- cardContainer.appendChild(playerThumbnailsContainer);
- cardContainer.appendChild(serverDetails);
-
- cardItem.appendChild(cardContainer);
-
- // Add the card to the server list
- const serverList = document.querySelector('#rbx-game-server-item-container');
- serverList.appendChild(cardItem);
- }
-
- /*********************************************************************************************************************************************************************************************************************************************
- The function for the notification function
-
- *********************************************************************************************************************************************************************************************************************************************/
-
- // Create the toast container
- const toastContainer = document.createElement('div');
- toastContainer.id = 'toast-container';
- document.body.appendChild(toastContainer);
-
- // Define toast styles
- const styles = `
- #toast-container {
- position: fixed;
- top: 20px;
- right: 20px;
- z-index: 9999;
- display: flex;
- flex-direction: column;
- align-items: flex-end;
- }
-
- .toast {
- position: relative;
- min-width: 250px;
- padding: 15px 20px;
- margin-bottom: 10px;
- border-radius: 5px;
- color: white;
- font-family: 'Arial', sans-serif;
- font-size: 14px;
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
- opacity: 0;
- transform: translateX(100%);
- animation: slideIn 0.5s ease-out forwards, bounce 0.5s ease-out, fadeOut 0.5s ease-out 7.5s forwards;
- display: flex;
- align-items: center;
- }
-
- .toast.success {
- background-color: #4CAF50;
- }
-
- .toast.error {
- background-color: #F44336;
- }
-
- .toast.warning {
- background-color: #FF9800;
- }
-
- .toast.info {
- background-color: #2196F3;
- }
-
- .toast::after {
- content: '';
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- height: 4px;
- background-color: rgba(255, 255, 255, 0.5);
- animation: progressBar 8s linear forwards;
- }
-
- .toast .icon {
- margin-right: 10px;
- font-size: 20px;
- }
-
- @keyframes slideIn {
- to {
- opacity: 1;
- transform: translateX(0);
- }
- }
-
- @keyframes fadeOut {
- to {
- opacity: 0;
- transform: translateX(100%);
- }
- }
-
- @keyframes progressBar {
- to {
- width: 0;
- }
- }
-
- @keyframes bounce {
- 0%, 20%, 50%, 80%, 100% {
- transform: translateY(0);
- }
- 40% {
- transform: translateY(-20px);
- }
- 60% {
- transform: translateY(-10px);
- }
- }
- `;
-
- // Add styles to the document
- const styleSheet = document.createElement('style');
- styleSheet.type = 'text/css';
- styleSheet.innerText = styles;
- document.head.appendChild(styleSheet);
-
- // Function to create a toast
- function notifications(message, type, icon) {
- const toast = document.createElement('div');
- toast.className = `toast ${type}`;
-
- // Add icon
- const iconElement = document.createElement('span');
- iconElement.className = 'icon';
- iconElement.innerHTML = icon;
- toast.appendChild(iconElement);
-
- // Add message
- const messageElement = document.createElement('span');
- messageElement.innerText = message;
- toast.appendChild(messageElement);
-
- toastContainer.appendChild(toast);
-
- // Remove the toast after the animation ends
- setTimeout(() => {
- toast.remove();
- }, 8000);
- }
-
- /*********************************************************************************************************************************************************************************************************************************************
- End of function for the notification function
-
- *********************************************************************************************************************************************************************************************************************************************/
-
- const serverRegionsByIp = {
- "128.116.0.0": { city: "Hong Kong", country: { name: "Hong Kong", code: "HK" }, latitude: 22.3193, longitude: 114.1694 },
- "128.116.1.0": { city: "Los Angeles", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 34.0522, longitude: -118.2437 },
- "128.116.2.0": { city: "Warsaw", country: { name: "Poland", code: "PL" }, region: { name: "Mazowieckie", code: "14" }, latitude: 52.2297, longitude: 21.0122 },
- "128.116.3.0": { city: "Warsaw", country: { name: "Poland", code: "PL" }, region: { name: "Mazowieckie", code: "14" }, latitude: 52.2297, longitude: 21.0122 },
- "128.116.4.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
- "128.116.5.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.6.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
- "128.116.7.0": { city: "Mumbai", country: { name: "India", code: "IN" }, region: { name: "Mahārāshtra", code: "MH" }, latitude: 19.0760, longitude: 72.8777 },
- "128.116.8.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.9.0": { city: "Mumbai", country: { name: "India", code: "IN" }, region: { name: "Mahārāshtra", code: "MH" }, latitude: 19.0760, longitude: 72.8777 },
- "128.116.10.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.11.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.12.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.13.0": { city: "Amsterdam", country: { name: "Netherlands", code: "NL" }, region: { name: "Noord-Holland", code: "NH" }, latitude: 52.3676, longitude: 4.9041 },
- "128.116.14.0": { city: "Hong Kong", country: { name: "Hong Kong", code: "HK" }, latitude: 22.3193, longitude: 114.1694 },
- "128.116.15.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
- "128.116.16.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
- "128.116.17.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
- "128.116.18.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
- "128.116.19.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
- "128.116.20.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
- "128.116.21.0": { city: "Amsterdam", country: { name: "Netherlands", code: "NL" }, region: { name: "Noord-Holland", code: "NH" }, latitude: 52.3676, longitude: 4.9041 },
- "128.116.22.0": { city: "Atlanta", country: { name: "United States", code: "US" }, region: { name: "Georgia", code: "GA" }, latitude: 33.7490, longitude: -84.3880 },
- "128.116.23.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
- "128.116.24.0": { city: "Atlanta", country: { name: "United States", code: "US" }, region: { name: "Georgia", code: "GA" }, latitude: 33.7490, longitude: -84.3880 },
- "128.116.25.0": { city: "Atlanta", country: { name: "United States", code: "US" }, region: { name: "Georgia", code: "GA" }, latitude: 33.7490, longitude: -84.3880 },
- "128.116.26.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
- "128.116.27.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.28.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.29.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.30.0": { city: "Hong Kong", country: { name: "Hong Kong", code: "HK" }, latitude: 22.3193, longitude: 114.1694 },
- "128.116.31.0": { city: "Warsaw", country: { name: "Poland", code: "PL" }, region: { name: "Mazowieckie", code: "14" }, latitude: 52.2297, longitude: 21.0122 },
- "128.116.32.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
- "128.116.33.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
- "128.116.34.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.35.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
- "128.116.36.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
- "128.116.37.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
- "128.116.38.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
- "128.116.39.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.40.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.41.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.42.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.43.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.44.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.45.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
- "128.116.46.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.47.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.48.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.49.0": { city: "Los Angeles", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 34.0522, longitude: -118.2437 },
- "128.116.50.0": { city: "Singapore", country: { name: "Singapore", code: "SG" }, latitude: 1.3521, longitude: 103.8198 },
- "128.116.51.0": { city: "Sydney", country: { name: "Australia", code: "AU" }, region: { name: "New South Wales", code: "NSW" }, latitude: -33.8688, longitude: 151.2093 },
- "128.116.52.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.53.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.54.0": { city: "Amsterdam", country: { name: "Netherlands", code: "NL" }, region: { name: "Noord-Holland", code: "NH" }, latitude: 52.3676, longitude: 4.9041 },
- "128.116.55.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
- "128.116.56.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.57.0": { city: "San Jose", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3382, longitude: -121.8863 },
- "128.116.58.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
- "128.116.59.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
- "128.116.60.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
- "128.116.61.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.62.0": { city: "Seattle", country: { name: "United States", code: "US" }, region: { name: "Washington", code: "WA" }, latitude: 47.6062, longitude: -122.3321 },
- "128.116.63.0": { city: "Los Angeles", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 34.0522, longitude: -118.2437 },
- "128.116.64.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.65.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
- "128.116.66.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
- "128.116.67.0": { city: "San Jose", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3382, longitude: -121.8863 },
- "128.116.68.0": { city: "Santa Clara", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3541, longitude: -121.9552 },
- "128.116.69.0": { city: "Santa Clara", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3541, longitude: -121.9552 },
- "128.116.70.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.71.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.72.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
- "128.116.73.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
- "128.116.74.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.75.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.76.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.77.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.78.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.79.0": { city: "Singapore", country: { name: "Singapore", code: "SG" }, latitude: 1.3521, longitude: 103.8198 },
- "128.116.80.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.81.0": { city: "Santa Clara", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3541, longitude: -121.9552 },
- "128.116.82.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
- "128.116.83.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
- "128.116.84.0": { city: "Elk Grove Village", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 42.0039, longitude: -87.9706 },
- "128.116.85.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
- "128.116.86.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.87.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.88.0": { city: "Elk Grove Village", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 42.0039, longitude: -87.9706 },
- "128.116.89.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
- "128.116.90.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.91.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.92.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.93.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.94.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.95.0": { city: "Dallas", country: { name: "United States", code: "US" }, region: { name: "Texas", code: "TX" }, latitude: 32.7767, longitude: -96.7970 },
- "128.116.96.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.97.0": { city: "Singapore", country: { name: "Singapore", code: "SG" }, latitude: 1.3521, longitude: 103.8198 },
- "128.116.98.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.99.0": { city: "Atlanta", country: { name: "United States", code: "US" }, region: { name: "Georgia", code: "GA" }, latitude: 33.7490, longitude: -84.3880 },
- "128.116.100.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.101.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.102.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.103.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.104.0": { city: "Mumbai", country: { name: "India", code: "IN" }, region: { name: "Mahārāshtra", code: "MH" }, latitude: 19.0760, longitude: 72.8777 },
- "128.116.105.0": { city: "Santa Clara", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3541, longitude: -121.9552 },
- "128.116.106.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.107.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.108.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.109.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.110.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.111.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.112.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.113.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
- "128.116.114.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
- "128.116.115.0": { city: "Seattle", country: { name: "United States", code: "US" }, region: { name: "Washington", code: "WA" }, latitude: 47.6062, longitude: -122.3321 },
- "128.116.116.0": { city: "Los Angeles", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 34.0522, longitude: -118.2437 },
- "128.116.117.0": { city: "San Jose", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3382, longitude: -121.8863 },
- "128.116.118.0": { city: "Hong Kong", country: { name: "Hong Kong", code: "HK" }, latitude: 22.3193, longitude: 114.1694 },
- "128.116.119.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
- "128.116.120.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
- "128.116.121.0": { city: "Amsterdam", country: { name: "Netherlands", code: "NL" }, region: { name: "Noord-Holland", code: "NH" }, latitude: 52.3676, longitude: 4.9041 },
- "128.116.122.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
- "128.116.123.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
- "128.116.124.0": { city: "Warsaw", country: { name: "Poland", code: "PL" }, region: { name: "Mazowieckie", code: "14" }, latitude: 52.2297, longitude: 21.0122 },
- "128.116.125.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
- "128.116.126.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
- "128.116.127.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
- };
-
- // Main function does nothing lmao but its ere i guees
- function main() {
- const gameIdMatch = window.location.pathname.match(/\/games\/(\d+)\//);
- if (!gameIdMatch) {
- console.error("Game ID not found in URL!");
- return;
- }
-
- const gameId = gameIdMatch[1];
- }
-
- /*******************************************************
- name of function: Initiate the observer
- description: Start observing the document for changes
- *******************************************************/
-
- // Start observing the document for changes
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
- main();
- })();