// ==UserScript== // @name RoLocate // @namespace https://oqarshi.github.io/ // @version 25.2 // @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 CC-BY-4.0; https://creativecommons.org/licenses/by/4.0/ // @icon data:image/jpeg;base64,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 // @grant GM_xmlhttpRequest // @downloadURL none // ==/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: 420px; 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, tooltips, experimental status, and explanations for each button const buttonData = [{ name: "Smallest Servers", tooltip: "**Reverses the order of the server list.** The emptiest servers will be displayed first.", experimental: false }, { name: "Available Space", tooltip: "**Filters out servers which are full.** Servers with space will only be shown.", experimental: false }, { 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.", experimental: false }, { 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.", experimental: false }, { 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.", experimental: true, experimentalExplanation: "**Experimental**: Still in development and testing. Ping may be inaccurate sometimes because of the Roblox API." }, { 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.", experimental: true, experimentalExplanation: "**Experimental**: Still in development and testing. it may be less accurate in regions with fewer Roblox servers" }, { name: "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.", experimental: false }, { name: "Locate Player", tooltip: "**Finds and joins the server a user is playing on if they are playing this particular game.** Note: May take a while for very popular games.", experimental: true, experimentalExplanation: "**Experimental**: Still in development and testing. It may not be accurate with popular avatars, such as the default Roblox avatars." }, { name: "About", tooltip: "**The Credits.** Rolocate was created by Oqarshi. Special thanks to BTRoblox ❤️. Enjoy using Rolocate!", experimental: false } ]; // Create buttons with unique names, tooltips, experimental status, and explanations 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, "$1"); const buttonText = document.createElement('p'); buttonText.style.cssText = ` margin: 0; color: white; font-size: 16px; `; buttonText.textContent = data.name; // Add "EXP" label if the button is experimental if (data.experimental) { const expLabel = document.createElement('span'); expLabel.textContent = 'EXP'; expLabel.style.cssText = ` margin-left: 8px; color: gold; font-size: 12px; font-weight: bold; background-color: rgba(255, 215, 0, 0.1); padding: 2px 6px; border-radius: 3px; `; buttonText.appendChild(expLabel); } // Add experimental explanation tooltip (left side) let experimentalTooltip = null; if (data.experimental) { experimentalTooltip = document.createElement('div'); experimentalTooltip.className = 'experimental-tooltip'; experimentalTooltip.style.cssText = ` display: none; position: absolute; top: 0; right: 200px; width: 200px; background-color: #191B1D; color: white; padding: 5px; border-radius: 5px; font-size: 14px; white-space: pre-wrap; z-index: 1001; `; // Function to replace **text** with bold and gold styled text const formatText = (text) => { return text.replace(/\*\*(.*?)\*\*/g, '$1'); }; // Apply the formatting to the experimental explanation experimentalTooltip.innerHTML = formatText(data.experimentalExplanation); buttonContainer.appendChild(experimentalTooltip); } buttonContainer.appendChild(tooltip); buttonContainer.appendChild(buttonText); buttonContainer.addEventListener('mouseover', () => { tooltip.style.display = 'block'; if (data.experimental) { experimentalTooltip.style.display = 'block'; } buttonContainer.style.backgroundColor = '#4A4C4E'; // Hover effect }); buttonContainer.addEventListener('mouseout', () => { tooltip.style.display = 'none'; if (data.experimental) { experimentalTooltip.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: find_user_server_tab(); break; case 8: 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 overlay (backdrop) const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 9999; opacity: 0; transition: opacity 0.3s ease; `; document.body.appendChild(overlay); // 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: 10px; z-index: 10000; box-shadow: 0 0 15px rgba(0, 0, 0, 0.7); display: flex; flex-direction: column; align-items: center; gap: 15px; width: 300px; opacity: 0; transition: opacity 0.3s ease, transform 0.3s ease; `; // Add a close button in the top-right corner (bigger size) const closeButton = document.createElement('button'); closeButton.innerHTML = '×'; // Using '×' for the close icon closeButton.style.cssText = ` position: absolute; top: 10px; right: 10px; background: transparent; border: none; color: #ffffff; font-size: 24px; /* Increased font size */ cursor: pointer; width: 36px; /* Increased size */ height: 36px; /* Increased size */ border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: background-color 0.3s ease, color 0.3s ease; `; closeButton.addEventListener('mouseenter', () => { closeButton.style.backgroundColor = 'rgba(255, 255, 255, 0.1)'; closeButton.style.color = '#ff4444'; }); closeButton.addEventListener('mouseleave', () => { closeButton.style.backgroundColor = 'transparent'; closeButton.style.color = '#ffffff'; }); // Add a title const title = document.createElement('h3'); title.textContent = 'Select Max Player Count'; title.style.cssText = ` color: white; margin: 0; font-size: 18px; font-weight: 500; `; popup.appendChild(title); // Add a slider with improved functionality and styling const slider = document.createElement('input'); slider.type = 'range'; slider.min = '1'; slider.max = '100'; slider.value = '1'; // Default value slider.step = '1'; // Step for better accuracy slider.style.cssText = ` width: 80%; cursor: pointer; margin: 10px 0; -webkit-appearance: none; /* Remove default styling */ background: transparent; `; // Custom slider track slider.style.background = ` linear-gradient( to right, #00A2FF 0%, #00A2FF ${slider.value}%, #444 ${slider.value}%, #444 100% ); border-radius: 5px; height: 6px; `; // Custom slider thumb slider.style.setProperty('--thumb-size', '20px'); /* Larger thumb */ slider.style.setProperty('--thumb-color', '#00A2FF'); slider.style.setProperty('--thumb-hover-color', '#0088cc'); slider.style.setProperty('--thumb-border', '2px solid #fff'); slider.style.setProperty('--thumb-shadow', '0 0 5px rgba(0, 0, 0, 0.5)'); slider.addEventListener('input', () => { slider.style.background = ` linear-gradient( to right, #00A2FF 0%, #00A2FF ${slider.value}%, #444 ${slider.value}%, #444 100% ); `; sliderValue.textContent = slider.value; // Update the displayed value }); // Keyboard support for better accuracy (fixed to increment/decrement by 1) slider.addEventListener('keydown', (e) => { e.preventDefault(); // Prevent default behavior (which might cause jumps) let newValue = parseInt(slider.value, 10); if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { newValue = Math.max(1, newValue - 1); // Decrease by 1 } else if (e.key === 'ArrowRight' || e.key === 'ArrowUp') { newValue = Math.min(100, newValue + 1); // Increase by 1 } slider.value = newValue; slider.dispatchEvent(new Event('input')); // Trigger input event to update UI }); 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; font-weight: bold; `; popup.appendChild(sliderValue); // Add a submit button with dark, blackish style const submitButton = document.createElement('button'); submitButton.textContent = 'Search'; submitButton.style.cssText = ` padding: 8px 20px; font-size: 16px; background-color: #1a1a1a; /* Dark blackish color */ color: white; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease, transform 0.2s ease; `; submitButton.addEventListener('mouseenter', () => { submitButton.style.backgroundColor = '#333'; /* Slightly lighter on hover */ submitButton.style.transform = 'scale(1.05)'; }); submitButton.addEventListener('mouseleave', () => { submitButton.style.backgroundColor = '#1a1a1a'; submitButton.style.transform = 'scale(1)'; }); // Add a yellow box with a tip under the submit button const tipBox = document.createElement('div'); tipBox.style.cssText = ` width: 100%; padding: 10px; background-color: rgba(255, 204, 0, 0.15); border-radius: 5px; text-align: center; font-size: 14px; color: #ffcc00; transition: background-color 0.3s ease; `; tipBox.textContent = 'Tip: Using arrow keys would be more accurate.'; tipBox.addEventListener('mouseenter', () => { tipBox.style.backgroundColor = 'rgba(255, 204, 0, 0.25)'; }); tipBox.addEventListener('mouseleave', () => { tipBox.style.backgroundColor = 'rgba(255, 204, 0, 0.15)'; }); popup.appendChild(tipBox); // Append the popup to the body document.body.appendChild(popup); // Fade in the overlay and popup setTimeout(() => { overlay.style.opacity = '1'; popup.style.opacity = '1'; popup.style.transform = 'translate(-50%, -50%) scale(1)'; }, 10); /******************************************************* name of function: fadeOutAndRemove description: Fades out and removes the popup and overlay. *******************************************************/ function fadeOutAndRemove(popup, overlay) { popup.style.opacity = '0'; popup.style.transform = 'translate(-50%, -50%) scale(0.9)'; overlay.style.opacity = '0'; setTimeout(() => { popup.remove(); overlay.remove(); }, 300); // Match the duration of the transition } // Close the popup when clicking outside overlay.addEventListener('click', () => { fadeOutAndRemove(popup, overlay); }); // Close the popup when the close button is clicked closeButton.addEventListener('click', () => { fadeOutAndRemove(popup, overlay); }); // Handle submit button click submitButton.addEventListener('click', () => { const maxPlayers = parseInt(slider.value, 10); if (!isNaN(maxPlayers) && maxPlayers > 0) { filterServersByPlayerCount(maxPlayers); fadeOutAndRemove(popup, overlay); } else { notifications('Error: Please enter a number greater than 0', 'error', '⚠️'); } }); popup.appendChild(submitButton); popup.appendChild(closeButton); } /******************************************************* 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() { notifications('Finding Random Server. Please wait 5-10 seconds', 'success', '🔎'); // 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 modal background */ .overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.85); /* Solid black overlay */ z-index: 1000; /* Ensure overlay is below the popup */ opacity: 0; /* Start invisible */ animation: fadeIn 0.3s ease forwards; /* Fade-in animation */ } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } /* Popup Container for the server region */ .filter-popup { background-color: #1e1e1e; /* Darker background */ color: #ffffff; /* White text */ padding: 25px; border-radius: 12px; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5); width: 320px; 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 */ border: 1px solid #444; /* Subtle border */ opacity: 0; /* Start invisible */ animation: fadeInPopup 0.3s ease 0.1s forwards; /* Fade-in animation with delay */ } @keyframes fadeInPopup { from { opacity: 0; transform: translate(-50%, -55%); /* Slight upward offset */ } to { opacity: 1; transform: translate(-50%, -50%); /* Center position */ } } /* Fade-out animation for overlay and popup */ .overlay.fade-out { animation: fadeOut 0.3s ease forwards; } .filter-popup.fade-out { animation: fadeOutPopup 0.3s ease forwards; } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } @keyframes fadeOutPopup { from { opacity: 1; transform: translate(-50%, -50%); /* Center position */ } to { opacity: 0; transform: translate(-50%, -55%); /* Slight upward offset */ } } /* Close Button for the server selector */ #closePopup { position: absolute; top: 5px; /* Reduced from 12px to 5px */ right: 1px; /* Reduced from 12px to 5px */ background: transparent; /* Transparent background */ border: none; color: #ffffff; /* White color */ font-size: 20px; cursor: pointer; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: background-color 0.3s ease, color 0.3s ease; } #closePopup:hover { background-color: rgba(255, 255, 255, 0.1); /* Light hover effect */ color: #ff4444; /* Red color on hover */ } /* Label */ .filter-popup label { display: block; margin-bottom: 12px; font-size: 16px; color: #ffffff; font-weight: 500; /* Slightly bolder text */ } /* Dropdown */ .filter-popup select { background-color: #333; /* Darker gray background */ color: #ffffff; /* White text */ padding: 10px; border-radius: 6px; border: 1px solid #555; /* Darker border */ width: 100%; margin-bottom: 12px; font-size: 14px; transition: border-color 0.3s ease; } .filter-popup select:focus { border-color: #888; /* Lighter border on focus */ outline: none; } /* Custom Input */ .filter-popup input[type="number"] { background-color: #333; /* Darker gray background */ color: #ffffff; /* White text */ padding: 10px; border-radius: 6px; border: 1px solid #555; /* Darker border */ width: 100%; margin-bottom: 12px; font-size: 14px; transition: border-color 0.3s ease; } .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: 10px 20px; border: 1px solid #666; /* Gray border */ border-radius: 6px; cursor: pointer; font-size: 14px; width: 100%; transition: background-color 0.3s ease, transform 0.2s ease; } #confirmServerCount:hover { background-color: #555; /* Lighter gray on hover */ transform: translateY(-1px); /* Slight lift effect */ } #confirmServerCount:active { transform: translateY(0); /* Reset lift effect on click */ } /* Highlighted server item */ .rbx-game-server-item.highlighted { border: 2px solid #4caf50; /* Green border */ border-radius: 8px; background-color: rgba(76, 175, 80, 0.1); /* Subtle green background */ } /* Disabled fetch button */ .fetch-button:disabled { opacity: 0.5; cursor: not-allowed; } /* Popup Header */ .popup-header { margin-bottom: 24px; text-align: left; padding: 16px; background-color: rgba(255, 255, 255, 0.05); /* Subtle background for contrast */ border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.1); /* Subtle border */ transition: background-color 0.3s ease, border-color 0.3s ease; } .popup-header:hover { background-color: rgba(255, 255, 255, 0.08); /* Slightly brighter on hover */ border-color: rgba(255, 255, 255, 0.2); } .popup-header h3 { margin: 0 0 12px 0; font-size: 22px; color: #ffffff; font-weight: 700; /* Bolder for emphasis */ letter-spacing: -0.5px; /* Tighter letter spacing for modern look */ } .popup-header p { margin: 0; font-size: 14px; color: #cccccc; line-height: 1.6; /* Improved line height for readability */ opacity: 0.9; /* Slightly transparent for a softer look */ } /* Popup Footer */ .popup-footer { margin-top: 20px; text-align: left; font-size: 14px; color: #ffcc00; /* Yellow color for warnings */ background-color: rgba(255, 204, 0, 0.15); /* Lighter yellow background */ padding: 12px; border-radius: 8px; border: 1px solid rgba(255, 204, 0, 0.15); /* Subtle border */ transition: background-color 0.3s ease, border-color 0.3s ease; } .popup-footer:hover { background-color: rgba(255, 204, 0, 0.25); /* Slightly brighter on hover */ border-color: rgba(255, 204, 0, 0.25); } .popup-footer p { margin: 0; line-height: 1.5; font-weight: 500; /* Slightly bolder for emphasis */ } /* Label */ .filter-popup label { display: block; margin-bottom: 12px; font-size: 15px; color: #ffffff; font-weight: 500; text-align: left; opacity: 0.9; /* Slightly transparent for a softer look */ transition: opacity 0.3s ease; } .filter-popup label:hover { opacity: 1; /* Fully opaque on hover */ } `; 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 = ` `; 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() { const overlay = document.querySelector('.overlay'); const popup = document.querySelector('.filter-popup'); // Add fade-out classes overlay.classList.add('fade-out'); popup.classList.add('fade-out'); // Remove elements after animation completes setTimeout(() => { overlay.remove(); popup.remove(); }, 300); // Match the duration of the fade-out animation } } // 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'; filterContainer.style.display = 'flex'; filterContainer.style.gap = '15px'; filterContainer.style.alignItems = 'center'; filterContainer.style.justifyContent = 'center'; filterContainer.style.padding = '20px'; filterContainer.style.backgroundColor = '#1e1e1e'; filterContainer.style.borderRadius = '12px'; filterContainer.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)'; filterContainer.style.opacity = '0'; // Start invisible for fade-in animation filterContainer.style.transform = 'translateY(-20px)'; // Start slightly above for animation filterContainer.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; // Fade-in animation for the container setTimeout(() => { filterContainer.style.opacity = '1'; filterContainer.style.transform = 'translateY(0)'; }, 100); // Add a logo placeholder (you can replace this with your own image) const logo = document.createElement('img'); logo.src = 'data:image/jpeg;base64,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'; logo.style.width = '40px'; logo.style.height = '40px'; logo.style.borderRadius = '8px'; logo.style.marginRight = '10px'; logo.style.transition = 'transform 0.3s ease'; // Add hover effect to the logo logo.addEventListener('mouseover', () => { logo.style.transform = 'scale(1.1)'; }); logo.addEventListener('mouseout', () => { logo.style.transform = 'scale(1)'; }); filterContainer.appendChild(logo); const createDropdown = (id, placeholder) => { const dropdown = document.createElement('select'); dropdown.id = id; dropdown.innerHTML = ``; dropdown.style.backgroundColor = '#333'; dropdown.style.color = '#fff'; dropdown.style.borderRadius = '8px'; dropdown.style.padding = '10px 15px'; dropdown.style.fontSize = '14px'; dropdown.style.border = 'none'; dropdown.style.outline = 'none'; dropdown.style.cursor = 'pointer'; dropdown.style.transition = 'all 0.3s ease'; dropdown.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)'; dropdown.style.opacity = '0'; // Start invisible for fade-in animation dropdown.style.transform = 'translateY(-10px)'; // Start slightly above for animation // Fade-in animation for the dropdown setTimeout(() => { dropdown.style.opacity = '1'; dropdown.style.transform = 'translateY(0)'; }, 300); dropdown.addEventListener('mouseover', () => { dropdown.style.backgroundColor = '#444'; dropdown.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)'; dropdown.style.transform = 'scale(1.02)'; }); dropdown.addEventListener('mouseout', () => { dropdown.style.backgroundColor = '#333'; dropdown.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)'; dropdown.style.transform = 'scale(1)'; }); dropdown.addEventListener('focus', () => { dropdown.style.backgroundColor = '#444'; dropdown.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)'; }); dropdown.addEventListener('blur', () => { dropdown.style.backgroundColor = '#333'; dropdown.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)'; }); // Add transition when an option is selected dropdown.addEventListener('change', () => { dropdown.style.transform = 'scale(1.05)'; setTimeout(() => { dropdown.style.transform = 'scale(1)'; }, 200); // Reset after 200ms }); return dropdown; }; const countryDropdown = createDropdown('countryFilter', 'All Countries'); const cityDropdown = createDropdown('cityFilter', 'All Cities'); // 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 = ''; 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 const cities = Object.keys(cityCounts); if (cities.length === 1) { cityDropdown.value = cities[0]; } // Add a transition effect when the city dropdown updates cityDropdown.style.opacity = '0'; cityDropdown.style.transform = 'translateY(-10px)'; setTimeout(() => { cityDropdown.style.opacity = '1'; cityDropdown.style.transform = 'translateY(0)'; }, 100); } }); 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) { disableLoadMoreButton(true); disableFilterButton(true); notifications("Retrieving Location...", "success", "🌎") // Ask for the user's location const userLocation = await getUserLocation(); if (!userLocation) { notifications('Error: Unable to fetch your location. Please enable location access.', 'error', '⚠️'); disableFilterButton(false); return; } // Fetch 50 servers const servers = await fetchPublicServers(gameId, 50); if (servers.length === 0) { notifications('Error: No servers found. Please try again later.', 'error', '⚠️'); disableFilterButton(false); 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 showLoadingOverlay(); 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. Filter button disabled.', 'error', '⚠️'); Loadingbar(false); } 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 = ` ${thumbnailsContainer.outerHTML}
${server.playing} of ${server.maxPlayers} people max
Ping: ${server.ping}ms
${location.city}, ${location.country.name}
FPS: ${Math.round(server.fps)}
`; const joinButton = cardItem.querySelector(".rbx-game-server-join"); joinButton.addEventListener("click", () => { console.log(`Roblox.GameLauncher.joinGameInstance(${gameId}, "${server.id}")`); showLoadingOverlay(); 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 showLoadingOverlay(); 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 find_user_server_tab() { // Create the overlay (backdrop) const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 9999; opacity: 0; transition: opacity 0.3s ease; `; document.body.appendChild(overlay); // 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: 10px; z-index: 10000; box-shadow: 0 0 15px rgba(0, 0, 0, 0.7); display: flex; flex-direction: column; align-items: center; gap: 15px; width: 300px; opacity: 0; transition: opacity 0.3s ease, transform 0.3s ease; `; // Add a close button in the top-right corner (bigger size) const closeButton = document.createElement('button'); closeButton.innerHTML = '×'; // Using '×' for the close icon closeButton.style.cssText = ` position: absolute; top: 10px; right: 10px; background: transparent; border: none; color: #ffffff; font-size: 24px; /* Increased font size */ cursor: pointer; width: 36px; /* Increased size */ height: 36px; /* Increased size */ border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: background-color 0.3s ease, color 0.3s ease; `; closeButton.addEventListener('mouseenter', () => { closeButton.style.backgroundColor = 'rgba(255, 255, 255, 0.1)'; closeButton.style.color = '#ff4444'; }); closeButton.addEventListener('mouseleave', () => { closeButton.style.backgroundColor = 'transparent'; closeButton.style.color = '#ffffff'; }); // Add a title const title = document.createElement('h3'); title.textContent = 'Enter Username'; title.style.cssText = ` color: white; margin: 0; font-size: 18px; font-weight: 500; `; popup.appendChild(title); // Add an input box for the username const usernameInput = document.createElement('input'); usernameInput.type = 'text'; usernameInput.placeholder = 'Username'; usernameInput.style.cssText = ` width: 80%; padding: 8px; font-size: 16px; border: 1px solid #444; border-radius: 5px; background-color: #1a1a1a; color: white; outline: none; `; popup.appendChild(usernameInput); // Add a confirm button with dark, blackish style const confirmButton = document.createElement('button'); confirmButton.textContent = 'Confirm'; confirmButton.style.cssText = ` padding: 8px 20px; font-size: 16px; background-color: #1a1a1a; /* Dark blackish color */ color: white; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease, transform 0.2s ease; `; confirmButton.addEventListener('mouseenter', () => { confirmButton.style.backgroundColor = '#333'; /* Slightly lighter on hover */ confirmButton.style.transform = 'scale(1.05)'; }); confirmButton.addEventListener('mouseleave', () => { confirmButton.style.backgroundColor = '#1a1a1a'; confirmButton.style.transform = 'scale(1)'; }); // Handle confirm button click confirmButton.addEventListener('click', () => { const username = usernameInput.value.trim(); if (username) { // Check if the username is between 3 and 20 characters if (username.length >= 3 && username.length <= 20) { // Call the function with the username FindPlayerGameServer(username); notifications("Searching for the user's server...", "info", ""); fadeOutAndRemove_7th(popup, overlay); } else { // Show an error notification if the username is not within the required length notifications('Error: Username must be between 3 and 20 characters', 'error', '⚠️'); } } else { notifications('Error: Please enter a username', 'error', '⚠️'); } }); // Append the popup to the body document.body.appendChild(popup); // Fade in the overlay and popup setTimeout(() => { overlay.style.opacity = '1'; popup.style.opacity = '1'; popup.style.transform = 'translate(-50%, -50%) scale(1)'; }, 10); /******************************************************* name of function: fadeOutAndRemove_7th description: Fades out and removes the popup and overlay. *******************************************************/ function fadeOutAndRemove_7th(popup, overlay) { popup.style.opacity = '0'; popup.style.transform = 'translate(-50%, -50%) scale(0.9)'; overlay.style.opacity = '0'; setTimeout(() => { popup.remove(); overlay.remove(); }, 300); // Match the duration of the transition } // Close the popup when clicking outside overlay.addEventListener('click', () => { fadeOutAndRemove_7th(popup, overlay); }); // Close the popup when the close button is clicked closeButton.addEventListener('click', () => { fadeOutAndRemove_7th(popup, overlay); }); popup.appendChild(confirmButton); popup.appendChild(closeButton); } async function FindPlayerGameServer(playerName) { disableLoadMoreButton(); Loadingbar(true); disableFilterButton(true); const wait = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds)); const fetchData = async (url, options = {}) => { if (!options.headers) options.headers = {}; return fetch(url, options) .then(response => response.json()) .catch(error => console.error("Fetch error:", error)); }; const fetchDataGM = (url, options = {}) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: options.method || 'GET', url: url, headers: options.headers || {}, anonymous: true, // Prevents sending cookies nocache: true, // Prevents caching onload: function(response) { try { const parsedData = JSON.parse(response.responseText); resolve(parsedData); } catch (error) { console.error("JSON parsing error:", error); reject(error); } }, onerror: function(error) { console.error("Request error:", error); reject(error); } }); }); }; console.log(`Initiating search for player: ${playerName}`); const gameId = window.location.href.split("/")[4]; console.log(`Game ID identified: ${gameId}`); let userId; try { console.log(`Retrieving user ID for player: ${playerName}`); const userProfile = await fetch(`https://www.roblox.com/users/profile?username=${playerName}`); if (!userProfile.ok) { notifications("Error: User does not exist on Roblox!", "error", "⚠️"); Loadingbar(false); disableFilterButton(false); throw `Player "${playerName}" not found`; } userId = userProfile.url.match(/\d+/)[0]; console.log(`User ID retrieved: ${userId}`); } catch (error) { console.error("Error:", error); return `Error: ${error}`; } console.log(`Fetching avatar thumbnail for user ID: ${userId}`); const avatarThumbnail = (await fetchData(`https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds=${userId}&format=Png&size=150x150`)).data[0].imageUrl; console.log(`Avatar thumbnail URL: ${avatarThumbnail}`); let pageCursor = null; let playerFound = false; let totalServersChecked = 0; let startTime = Date.now(); // Show the search progress popup with the player's thumbnail const progressPopup = showSearchProgressPopup(avatarThumbnail); while (true) { let apiUrl = `https://games.roblox.com/v1/games/${gameId}/servers/0?limit=100`; if (pageCursor) apiUrl += "&cursor=" + pageCursor; console.log(`Accessing servers with URL: ${apiUrl}`); const serverList = await fetchDataGM(apiUrl, { credentials: "omit" }).catch(() => null); if (serverList && serverList.data) { console.log(`Discovered ${serverList.data.length} servers in this set.`); for (let index = 0; index < serverList.data.length; index++) { const server = serverList.data[index]; if (!playerFound) { totalServersChecked++; // Calculate time elapsed const timeElapsed = Math.floor((Date.now() - startTime) / 1000); // Update the progress popup updateSearchProgressPopup( progressPopup, totalServersChecked, timeElapsed ); if (server.playerTokens.length === 0) { console.log(`Server ${index + 1} is empty. Proceeding to next server.`); continue; } console.log(`Inspecting server ${index + 1} hosting ${server.playing} players...`); let thumbnailData; while (true) { let requestBody = []; const generateRequestEntry = (playerToken) => ({ requestId: `0:${playerToken}:AvatarHeadshot:150x150:png:regular`, type: "AvatarHeadShot", targetId: 0, token: playerToken, format: "png", size: "150x150" }); server.playerTokens.forEach(token => { requestBody.push(generateRequestEntry(token)); }); try { console.log(`Fetching thumbnails for ${server.playerTokens.length} player(s)...`); thumbnailData = await fetchData("https://thumbnails.roblox.com/v1/batch", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, body: JSON.stringify(requestBody) }); console.log("Thumbnail Data Response:", thumbnailData); break; } catch (error) { console.error("Thumbnail retrieval error:", error); await wait(1000); } } if (!thumbnailData.data) { console.log("No thumbnail data available. Moving to next server."); continue; } for (let thumbIndex = 0; thumbIndex < thumbnailData.data.length; thumbIndex++) { const thumbnail = thumbnailData.data[thumbIndex]; if (thumbnail && thumbnail.imageUrl === avatarThumbnail) { playerFound = true; console.log(`Player located in server ${index + 1}!`); notifications("Found User's Server! Joining Server...", "success", "🚀"); showLoadingOverlay(); Roblox.GameLauncher.joinGameInstance(gameId, server.id); fadeOutAndRemove_7th_progress(progressPopup.popup, progressPopup.overlay); Loadingbar(false); disableFilterButton(false); return { gameId, serverId: server.id, currentPlayers: server.playing, maximumPlayers: server.maxPlayers, }; } } } else { break; } } pageCursor = serverList.nextPageCursor; if (!pageCursor || playerFound) break; else { console.log("Pausing for 1 seconds before next server batch..."); await wait(1); } } else { console.log("Server fetch failed. Retrying in 10 seconds..."); notifications("Got rate limited. Waiting 10 seconds...", "info", "❗") await wait(10000); } } if (!playerFound) { // Wait for 2 seconds before calling another function setTimeout(() => { fadeOutAndRemove_7th_progress(progressPopup.popup, progressPopup.overlay); notifications("User not found playing this game!", "error", "⚠️"); }, 2000); // 2000 milliseconds = 2 seconds // Existing logic (unchanged) Loadingbar(false); disableFilterButton(false); console.log(`Player not found in the searched servers (${totalServersChecked} servers checked)`); // Update the progress popup with the final count updateSearchProgressPopup( progressPopup, totalServersChecked, Math.floor((Date.now() - startTime) / 1000), true ); return `Player not found in the searched servers (${totalServersChecked} servers checked)`; } } /******************************************************* name of function: showSearchProgressPopup description: Creates and displays a popup showing the search progress. *******************************************************/ function showSearchProgressPopup(avatarThumbnail) { // Create the overlay (backdrop) const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 9999; opacity: 0; transition: opacity 0.3s ease; `; document.body.appendChild(overlay); // Create the popup container const popup = document.createElement('div'); popup.className = 'search-progress-popup'; popup.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: rgb(30, 32, 34); padding: 20px; border-radius: 10px; z-index: 10000; /* Higher than overlay */ box-shadow: 0 0 15px rgba(0, 0, 0, 0.7); display: flex; flex-direction: column; align-items: center; gap: 15px; width: 300px; opacity: 0; /* Start invisible */ transition: opacity 0.3s ease, transform 0.3s ease; `; // Add a title const title = document.createElement('h3'); title.textContent = 'Searching for Player...'; title.style.cssText = ` color: white; margin: 0; font-size: 18px; font-weight: 500; `; popup.appendChild(title); // Add the player's thumbnail const thumbnail = document.createElement('img'); thumbnail.src = avatarThumbnail; thumbnail.style.cssText = ` width: 100px; height: 100px; border-radius: 50%; object-fit: cover; `; popup.appendChild(thumbnail); // Add a progress text for servers searched const serversSearchedText = document.createElement('p'); serversSearchedText.textContent = 'Servers searched: 0'; serversSearchedText.style.cssText = ` color: white; margin: 0; font-size: 16px; `; popup.appendChild(serversSearchedText); // Add a text for time elapsed const timeElapsedText = document.createElement('p'); timeElapsedText.textContent = 'Time elapsed: 0s'; timeElapsedText.style.cssText = ` color: white; margin: 0; font-size: 16px; `; popup.appendChild(timeElapsedText); // Append the popup to the body document.body.appendChild(popup); // Fade in the overlay and popup setTimeout(() => { overlay.style.opacity = '1'; popup.style.opacity = '1'; popup.style.transform = 'translate(-50%, -50%) scale(1)'; }, 10); return { popup, overlay, serversSearchedText, timeElapsedText, }; } /******************************************************* name of function: updateSearchProgressPopup description: Updates the search progress popup with the current count. *******************************************************/ function updateSearchProgressPopup( progressPopup, totalServersChecked, timeElapsed, isFinal = false ) { progressPopup.serversSearchedText.textContent = `Servers searched: ${totalServersChecked}`; progressPopup.timeElapsedText.textContent = `Time elapsed: ${timeElapsed}s`; if (isFinal) { progressPopup.serversSearchedText.textContent += ' (Search completed)'; } } /******************************************************* name of function: fadeOutAndRemove description: Fades out and removes the popup and overlay. *******************************************************/ function fadeOutAndRemove_7th_progress(popup, overlay) { popup.style.opacity = '0'; popup.style.transform = 'translate(-50%, -50%) scale(0.9)'; overlay.style.opacity = '0'; setTimeout(() => { popup.remove(); overlay.remove(); }, 300); // Match the duration of the transition } /********************************************************************************************************************************************************************************************************************************************* Functions for the 9th 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 = `
×
Rolocate: Version 24.7

Credits

This project was created by:

`; // 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 *********************************************************************************************************************************************************************************************************************************************/ function showFirstTimePopup() { // Check if the localStorage item exists if (!localStorage.getItem('firstTimeDownload')) { // Inject CSS for the popup const css = ` .first-time-popup { display: flex; position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.9); justify-content: center; align-items: center; z-index: 1000; opacity: 0; animation: fadeIn 0.5s ease-in-out forwards; } .first-time-popup-content { background-color: #1a1a1a; padding: 30px; border-radius: 15px; width: 350px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); text-align: center; position: relative; color: #fff; transform: scale(0.9); animation: scaleUp 0.5s ease-in-out forwards; } .first-time-popup-content h2 { margin-top: 0; color: #4da6ff; /* Blue color for a calm but important feel */ font-size: 28px; margin-bottom: 15px; } .first-time-popup-content p { font-size: 16px; color: #bbb; margin-bottom: 10px; } .first-time-popup-content .countdown { font-size: 16px; color: #aaa; margin-bottom: 20px; font-weight: bold; } .first-time-popup-content a { color: #4da6ff; text-decoration: none; transition: color 0.3s ease; } .first-time-popup-content a:hover { color: #80bfff; text-decoration: underline; } .first-time-popup-close { position: absolute; top: 15px; right: 15px; font-size: 28px; font-weight: bold; cursor: pointer; color: #fff; transition: color 0.3s ease; opacity: 0.5; /* Make the close button look disabled */ pointer-events: none; /* Disable clicking */ } .first-time-popup-close.active { opacity: 1; /* Make the close button fully visible */ pointer-events: auto; /* Enable clicking */ } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } @keyframes scaleUp { from { transform: scale(0.9); } to { transform: scale(1); } } @keyframes scaleDown { from { transform: scale(1); } to { transform: scale(0.9); } } `; // 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 = `
×

Welcome!

Please check out the FAQ website. It can save you a lot of headaches!

Closing is enabled in 5 seconds...
`; // Add the popup to the document const popupContainer = document.createElement('div'); popupContainer.innerHTML = popupHTML; document.body.appendChild(popupContainer); // Get the close button, popup, and countdown elements const closeButton = document.querySelector('.first-time-popup-close'); const popup = document.querySelector('.first-time-popup'); const countdownTimer = document.getElementById('countdown-timer'); // Start the countdown let countdown = 5; const countdownInterval = setInterval(() => { countdown--; countdownTimer.innerHTML = `${countdown}`; // Update the countdown text // If the countdown reaches 0, enable the close button if (countdown <= 0) { clearInterval(countdownInterval); // Stop the countdown closeButton.classList.add('active'); // Make the close button clickable countdownTimer.innerHTML = "0"; // Ensure it shows 0 } }, 1000); // Update every second // Add event listener to close the popup with animation closeButton.addEventListener('click', () => { popup.style.animation = 'fadeOut 0.5s ease-in-out forwards'; document.querySelector('.first-time-popup-content').style.animation = 'scaleDown 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 }); // Set the localStorage item to indicate the popup has been shown localStorage.setItem('firstTimeDownload', 'true'); } } /******************************************************* 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', () => { showLoadingOverlay(); 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); } /********************************************************************************************************************************************************************************************************************************************* Modernized Notification Function with Gradient and Smooth Animations *********************************************************************************************************************************************************************************************************************************************/ // 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: 99999; display: flex; flex-direction: column; align-items: flex-end; } .toast { position: relative; min-width: 300px; padding: 15px 20px; margin-bottom: 10px; border-radius: 12px; color: white; font-family: 'Arial', sans-serif; font-size: 14px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); opacity: 0; transform: translateX(100%); animation: slideIn 0.5s ease-out forwards, fadeOut 1s ease-out 7s forwards; display: flex; align-items: center; background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05)); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.1); overflow: hidden; /* Add this to clip the progress bar */ } .toast.success { background: linear-gradient(135deg, rgba(76, 175, 80, 0.9), rgba(76, 175, 80, 0.7)); } .toast.error { background: linear-gradient(135deg, rgba(244, 67, 54, 0.9), rgba(244, 67, 54, 0.7)); } .toast.warning { background: linear-gradient(135deg, rgba(255, 152, 0, 0.9), rgba(255, 152, 0, 0.7)); } .toast.info { background: linear-gradient(135deg, rgba(33, 150, 243, 0.9), rgba(33, 150, 243, 0.7)); } .toast::after { content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 4px; background-color: rgba(255, 255, 255, 0.3); animation: progressBar 8s linear forwards; border-radius: 0 0 12px 12px; /* Match the toast's border-radius */ } .toast .icon { margin-right: 10px; font-size: 20px; } @keyframes slideIn { to { opacity: 1; transform: translateX(0); } } @keyframes fadeOut { 0% { opacity: 1; transform: translateX(0); } 100% { opacity: 0; transform: translateX(100%); } } @keyframes progressBar { to { width: 0; } } `; // 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 *********************************************************************************************************************************************************************************************************************************************/ /********************************************************************************************************************************************************************************************************************************************* Launching Function *********************************************************************************************************************************************************************************************************************************************/ function showLoadingOverlay() { // Create the content div (no overlay background) const content = document.createElement('div'); content.style.position = 'fixed'; content.style.top = 'calc(50% - 15px)'; // Move 15px higher content.style.left = '50%'; content.style.transform = 'translate(-50%, -50%)'; // Center the box horizontally content.style.width = '400px'; content.style.height = '250px'; content.style.backgroundColor = '#1e1e1e'; // Dark background content.style.display = 'flex'; content.style.flexDirection = 'column'; content.style.justifyContent = 'center'; content.style.alignItems = 'center'; content.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.5)'; // Subtle shadow content.style.borderRadius = '12px'; // Slightly rounded corners content.style.zIndex = '1000000'; // z-index set to 1 million content.style.opacity = '0'; // Start with 0 opacity for fade-in content.style.transition = 'opacity 0.5s ease'; // Fade-in transition content.style.fontFamily = 'Arial, sans-serif'; // Clean font // Create the loading text const loadingText = document.createElement('p'); loadingText.textContent = 'Joining Roblox Game...'; loadingText.style.fontSize = '20px'; loadingText.style.color = '#fff'; // White text for contrast loadingText.style.marginTop = '20px'; // Spacing between image and text loadingText.style.fontWeight = 'bold'; // Bold text // Create the base64 image const base64Image = document.createElement('img'); base64Image.src = 'data:image/jpeg;base64,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'; base64Image.style.width = '80px'; // Slightly larger image base64Image.style.height = '80px'; // Slightly larger image base64Image.style.borderRadius = '8px'; // Rounded corners for the image // Create the loading boxes container const loadingBoxes = document.createElement('div'); loadingBoxes.style.display = 'flex'; loadingBoxes.style.alignItems = 'center'; loadingBoxes.style.justifyContent = 'center'; loadingBoxes.style.marginTop = '15px'; // Spacing between text and boxes // Create the three loading boxes for (let i = 0; i < 3; i++) { const box = document.createElement('div'); box.style.width = '10px'; box.style.height = '10px'; box.style.backgroundColor = '#fff'; // White boxes box.style.margin = '0 5px'; // Spacing between boxes box.style.borderRadius = '2px'; // Slightly rounded corners box.style.animation = `pulse 1.2s ${i * 0.2}s infinite`; // Animation with delay loadingBoxes.appendChild(box); } // Define the pulse animation using CSS const style = document.createElement('style'); style.textContent = ` @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.5); } } `; document.head.appendChild(style); // Add the animation to the document // Append the image, text, and loading boxes to the content div content.appendChild(base64Image); content.appendChild(loadingText); content.appendChild(loadingBoxes); // Append the content div to the body document.body.appendChild(content); // Trigger fade-in animation setTimeout(() => { content.style.opacity = '1'; }, 10); // Small delay to trigger the transition // Remove the content after 8 seconds with fade-out animation setTimeout(() => { content.style.opacity = '0'; // Fade out setTimeout(() => { document.body.removeChild(content); // Remove after fade-out completes }, 500); // Wait for the fade-out transition to finish }, 8000); // 8000 milliseconds = 8 seconds } /********************************************************************************************************************************************************************************************************************************************* End of function for the launching 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: "New York City", country: { name: "United States", code: "US" }, region: { name: "New York", code: "NY" }, latitude: 40.7128, longitude: -74.0060 }, "128.116.33.0": { city: "London", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5072, longitude: 0.1276 }, "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: "London", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5072, longitude: 0.1276 }, "128.116.36.0": { city: "London", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5072, longitude: 0.1276 }, "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: "London", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5072, longitude: 0.1276 }, "128.116.73.0": { city: "London", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5072, longitude: 0.1276 }, "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: "London", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5072, longitude: 0.1276 }, "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: "London", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5072, longitude: 0.1276 }, "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 }); window.onload = showFirstTimePopup; main(); })();