RoLocate

Adds filter options to roblox server page. Alternative to paid extensions like RoPro, RoGold (Ultimate), RoQol, and RoKit.

目前为 2025-01-15 提交的版本。查看 最新版本

// ==UserScript==
// @name         RoLocate
// @namespace    https://oqarshi.github.io/
// @version      21.3
// @description  Adds filter options to roblox server page. Alternative to paid extensions like RoPro, RoGold (Ultimate), RoQol, and RoKit.
// @author       Oqarshi
// @match        https://www.roblox.com/games/*
// @license      MIT
// @icon         data:image/jpeg;base64,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
// @grant        GM_xmlhttpRequest
// ==/UserScript==



(function() {
	'use strict';

	/*********************************************************************************************************************************************************************************************************************************************
	                                                         This is all of the functions for the filter button and the popup for the 8 buttons does not include the functions for the 8 buttons

	*********************************************************************************************************************************************************************************************************************************************/

    function createPopup() {
    const popup = document.createElement('div');
    popup.className = 'server-filters-dropdown-box'; // Unique class name
    popup.style.cssText = `
        position: absolute;
        width: 210px;
        height: 382px;
        right: 0px;
        top: 30px;
        z-index: 1000;
        border-radius: 5px;
        background-color: rgb(30, 32, 34);
        display: flex;
        flex-direction: column;
        padding: 5px;
    `;

    // Create the header section
    const header = document.createElement('div');
    header.style.cssText = `
        display: flex;
        align-items: center;
        padding: 10px;
        border-bottom: 1px solid #444;
        margin-bottom: 5px;
    `;

    // Add the logo (base64 image)
    const logo = document.createElement('img');
    logo.src = 'data:image/jpeg;base64,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'; // Replace with your base64 logo
    logo.style.cssText = `
        width: 24px;
        height: 24px;
        margin-right: 10px;
    `;

    // Add the title
    const title = document.createElement('span');
    title.textContent = 'RoLocate';
    title.style.cssText = `
        color: white;
        font-size: 18px;
        font-weight: bold;
    `;

    // Append logo and title to the header
    header.appendChild(logo);
    header.appendChild(title);

    // Append the header to the popup
    popup.appendChild(header);

    // Define unique names and tooltips for each button
    const buttonData = [{
            name: "Smallest Servers",
            tooltip: "**Reverses the order of the server list.** The emptiest servers will be displayed first."
        },
        {
            name: "Available Space",
            tooltip: "**Filters out servers which are full.** Servers with space will only be shown."
        },
        {
            name: "Player Count",
            tooltip: "**Roblox Locator finds servers with your specified player count or fewer.** Searching for up to 3 minutes. If no exact match is found, it shows servers closest to the target."
        },
        {
            name: "Random Shuffle",
            tooltip: "**Display servers in a completely random order.** Shows servers with space and servers with low player counts in a randomized order."
        },
        {
            name: "Server Region",
            tooltip: "**Filters servers by region.** Offering more accuracy than 'Best Connection' in areas with fewer Roblox servers, like India, or in games with high player counts."
        },
        {
            name: "Best Connection",
            tooltip: "**Automatically joins the fastest servers for you.** However, it may be less accurate in regions with fewer Roblox servers, like India, or in games with large player counts."
        },
        {
            name: "Auto Join Small Server",
            tooltip: "**Automatically tries to join a server with a very low population.** On popular games servers may fill up very fast so you might not always get in alone."
        },
        {
            name: "About",
            tooltip: "**The Credits.** Rolocate was created by Oqarshi. Special thanks to BTRoblox ❤️. Enjoy using Rolocate!"
        }
    ];

    // Create buttons with unique names and tooltips
    buttonData.forEach((data, index) => {
        const buttonContainer = document.createElement('div');
        buttonContainer.className = 'server-filter-option';
        buttonContainer.style.cssText = `
            width: 190px;
            height: 30px;
            background-color: #393B3D;
            margin: 5px;
            border-radius: 5px;
            padding: 3.5px;
            position: relative;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background-color 0.3s ease;
        `;

        const tooltip = document.createElement('div');
        tooltip.className = 'filter-tooltip';
        tooltip.style.cssText = `
            display: none;
            position: absolute;
            top: -10px;
            left: 200px;
            width: auto;
            inline-size: 200px;
            height: auto;
            background-color: #191B1D;
            color: white;
            padding: 5px;
            border-radius: 5px;
            white-space: pre-wrap;
            font-size: 14px;
        `;

        // Parse tooltip text and replace **...** with bold HTML tags
        tooltip.innerHTML = data.tooltip.replace(/\*\*(.*?)\*\*/g, "<b style='color: #068f00;'>$1</b>");

        const buttonText = document.createElement('p');
        buttonText.style.cssText = `
            margin: 0;
            color: white;
            font-size: 16px;
        `;
        buttonText.textContent = data.name;

        buttonContainer.appendChild(tooltip);
        buttonContainer.appendChild(buttonText);

        buttonContainer.addEventListener('mouseover', () => {
            tooltip.style.display = 'block';
            buttonContainer.style.backgroundColor = '#4A4C4E'; // Hover effect
        });
        buttonContainer.addEventListener('mouseout', () => {
            tooltip.style.display = 'none';
            buttonContainer.style.backgroundColor = '#393B3D'; // Revert to original color
        });

        buttonContainer.addEventListener('click', () => {
            switch (index) {
                case 0:
                    smallest_servers();
                    break;
                case 1:
                    available_space_servers();
                    break;
                case 2:
                    player_count_tab();
                    break;
                case 3:
                    random_servers();
                    break;
                case 4:
                    createServerCountPopup((totalLimit) => {
                        rebuildServerList(gameId, totalLimit);
                    });
                    break;
                case 5:
                    rebuildServerList(gameId, 50, true);
                    break;
                case 6:
                    auto_join_small_server();
                    break;
                case 7:
                    credits();
                    break;
            }
        });

        popup.appendChild(buttonContainer);
    });

    return popup;
}


	/*******************************************************
	name of function: An Observer for the filter button
	description: to put the filter button on the page
	*******************************************************/

	// Wait for the server list options container to load
	const observer = new MutationObserver((mutations, obs) => {
		const serverListOptions = document.querySelector('.server-list-options');
		if (serverListOptions) {
			// Create the filter button
			const filterButton = document.createElement('a');
			filterButton.className = 'RL-filter-button'; // Unique class name
			filterButton.style.cssText = `
                color: white;
                font-weight: bold;
                text-decoration: none;
                cursor: pointer;
                margin-left: 10px;
                padding: 5px 10px;
                display: flex;
                align-items: center;
                gap: 5px;
                position: relative;
                margin-top: 4px
            `;
			filterButton.addEventListener('mouseover', () => {
				filterButton.style.textDecoration = 'underline';
			});
			filterButton.addEventListener('mouseout', () => {
				filterButton.style.textDecoration = 'none';
			});

			// Add the "Filter" text
			const buttonText = document.createElement('span');
			buttonText.className = 'RL-filter-text'; // Unique class name
			buttonText.textContent = 'Filters';
			filterButton.appendChild(buttonText);

			// Add the icon (three horizontal dashes)
			const icon = document.createElement('span');
			icon.className = 'RL-filter-icon'; // Unique class name
			icon.textContent = '≡';
			icon.style.cssText = `
                font-size: 18px;
            `;
			filterButton.appendChild(icon);

			// Append the button to the server list options container
			serverListOptions.appendChild(filterButton);

			// Handle click event to show/hide the popup
			let popup = null;
			filterButton.addEventListener('click', (event) => {
				event.stopPropagation(); // Prevent event bubbling
				if (popup) {
					popup.remove(); // Remove the popup if it already exists
					popup = null;
				} else {
					popup = createPopup();
					// Position the popup next to the filter button
					popup.style.top = `${filterButton.offsetHeight}px`;
					popup.style.left = '0';
					filterButton.appendChild(popup);
				}
			});

			// Close the popup when clicking outside
			document.addEventListener('click', (event) => {
				if (popup && !filterButton.contains(event.target)) {
					popup.remove();
					popup = null;
				}
			});

			// Stop observing once the button is added
			obs.disconnect();
		}
	})

	/*********************************************************************************************************************************************************************************************************************************************
	                                                         The End of: This is all of the functions for the filter button and the popup for the 8 buttons does not include the functions for the 8 buttons

	*********************************************************************************************************************************************************************************************************************************************/


	/*********************************************************************************************************************************************************************************************************************************************
	                                                         Functions for the 1st button

	*********************************************************************************************************************************************************************************************************************************************/


	/*******************************************************
	name of function: smallest_servers FIRST FUNCTION
	description: Fetches the smallest servers, disables the "Load More" button, shows a loading bar, and recreates the server cards.
	*******************************************************/
	async function smallest_servers() {
		// Disable the "Load More" button and show the loading bar
		Loadingbar(true);
		disableFilterButton(true);
		disableLoadMoreButton();

		// Get the game ID from the URL
		const gameId = window.location.pathname.split('/')[2];

		// Retry mechanism
		let retries = 3;
		let success = false;

		while (retries > 0 && !success) {
			try {
				// Fetch server data from the Roblox API
				const response = await fetch(`https://games.roblox.com/v1/games/${gameId}/servers/0?sortOrder=1&excludeFullGames=true&limit=100`);

				// Check if the response status is 429 (Too Many Requests)
				if (response.status === 429) {
					throw new Error('429: Too Many Requests');
				}

				const data = await response.json();

				// Process each server
				for (const server of data.data) {
					const {
						id: serverId,
						playerTokens,
						maxPlayers,
						playing
					} = server;

					// Pass the server data to the card creation function
					await rbx_card(serverId, playerTokens, maxPlayers, playing, gameId);
				}

				success = true; // Mark as successful if no errors occurred
			} catch (error) {
				retries--; // Decrement the retry count

				if (error.message === '429: Too Many Requests' && retries > 0) {
					console.log('Encountered a 429 error. Retrying in 10 seconds...');
					await new Promise(resolve => setTimeout(resolve, 10000)); // Wait for 10 seconds
				} else {
					console.error('Error fetching server data:', error);
					break; // Exit the loop if it's not a 429 error or no retries left
				}
			} finally {
				if (success || retries === 0) {
					// Hide the loading bar and enable the filter button
					Loadingbar(false);
					disableFilterButton(false);
				}
			}
		}
	}



	/*********************************************************************************************************************************************************************************************************************************************
	                                                         Functions for the 2nd button

	*********************************************************************************************************************************************************************************************************************************************/


	/*******************************************************
	name of function: available_space_servers
	description: Fetches servers with available space, disables the "Load More" button, shows a loading bar, and recreates the server cards.
	*******************************************************/
	async function available_space_servers() {
		// Disable the "Load More" button and show the loading bar
		Loadingbar(true);
		disableLoadMoreButton();
		disableFilterButton(true);

		// Get the game ID from the URL
		const gameId = window.location.pathname.split('/')[2];

		// Retry mechanism
		let retries = 3;
		let success = false;

		while (retries > 0 && !success) {
			try {
				// Fetch server data from the Roblox API
				const response = await fetch(`https://games.roblox.com/v1/games/${gameId}/servers/0?sortOrder=2&excludeFullGames=true&limit=100`);

				// Check if the response status is 429 (Too Many Requests)
				if (response.status === 429) {
					throw new Error('429: Too Many Requests');
				}

				const data = await response.json();

				// Process each server
				for (const server of data.data) {
					const {
						id: serverId,
						playerTokens,
						maxPlayers,
						playing
					} = server;

					// Pass the server data to the card creation function
					await rbx_card(serverId, playerTokens, maxPlayers, playing, gameId);
				}

				success = true; // Mark as successful if no errors occurred
			} catch (error) {
				retries--; // Decrement the retry count

				if (error.message === '429: Too Many Requests' && retries > 0) {
					console.log('Encountered a 429 error. Retrying in 10 seconds...');
					await new Promise(resolve => setTimeout(resolve, 10000)); // Wait for 10 seconds
				} else {
					console.error('Error fetching server data:', error);
					break; // Exit the loop if it's not a 429 error or no retries left
				}
			} finally {
				if (success || retries === 0) {
					// Hide the loading bar and enable the filter button
					Loadingbar(false);
					disableFilterButton(false);
				}
			}
		}
	}

	/*********************************************************************************************************************************************************************************************************************************************
	                                                         Functions for the 3rd button

	*********************************************************************************************************************************************************************************************************************************************/


	/*******************************************************
	name of function: player_count_tab
	description: Opens a popup for the user to select the max player count using a slider and filters servers accordingly.
	*******************************************************/
	function player_count_tab() {
		// Create the popup container
		const popup = document.createElement('div');
		popup.className = 'player-count-popup';
		popup.style.cssText = `
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: rgb(30, 32, 34);
        padding: 20px;
        border-radius: 5px;
        z-index: 10000;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 10px;
    `;

		// Add a title
		const title = document.createElement('h3');
		title.textContent = 'Select Max Player Count';
		title.style.cssText = `
        color: white;
        margin: 0;
        font-size: 18px;
    `;
		popup.appendChild(title);

		// Add a slider
		const slider = document.createElement('input');
		slider.type = 'range';
		slider.min = '1';
		slider.max = '100';
		slider.value = '1'; // Default value
		slider.style.cssText = `
        width: 200px;
        cursor: pointer;
    `;
		popup.appendChild(slider);

		// Add a display for the slider value
		const sliderValue = document.createElement('span');
		sliderValue.textContent = slider.value;
		sliderValue.style.cssText = `
        color: white;
        font-size: 16px;
    `;
		popup.appendChild(sliderValue);

		// Update the slider value display when the slider changes
		slider.addEventListener('input', () => {
			sliderValue.textContent = slider.value;
		});

		// Add a submit button
		const submitButton = document.createElement('button');
		submitButton.textContent = 'Search';
		submitButton.style.cssText = `
        padding: 5px 10px;
        font-size: 16px;
        background-color: #00A2FF;
        color: white;
        border: none;
        border-radius: 3px;
        cursor: pointer;
    `;
		popup.appendChild(submitButton);

		// Add a close button
		const closeButton = document.createElement('button');
		closeButton.textContent = 'Close';
		closeButton.style.cssText = `
        padding: 5px 10px;
        font-size: 16px;
        background-color: #555;
        color: white;
        border: none;
        border-radius: 3px;
        cursor: pointer;
    `;
		popup.appendChild(closeButton);

		// Append the popup to the body
		document.body.appendChild(popup);

		// Handle submit button click
		submitButton.addEventListener('click', () => {
			const maxPlayers = parseInt(slider.value, 10);
			if (!isNaN(maxPlayers) && maxPlayers > 0) {
				filterServersByPlayerCount(maxPlayers);
				popup.remove();
			} else {
				notifications('Error: Please enter a number greater than 0', 'error', '⚠️');
			}
		});
		// Handle close button click
		closeButton.addEventListener('click', () => {
			popup.remove();
		});

		// Close the popup when clicking outside
		document.addEventListener('click', (event) => {
			if (!popup.contains(event.target)) {
				popup.remove();
			}
		});
	}

	/*******************************************************
	name of function: fetchServersWithRetry
	description: Fetches server data with retry logic and a delay between requests to avoid rate-limiting.
	Uses GM_xmlhttpRequest instead of fetch.
	*******************************************************/
	async function fetchServersWithRetry(url, retries = 15, currentDelay = 750) {
		return new Promise((resolve, reject) => {
			GM_xmlhttpRequest({
				method: 'GET',
				url: url,
				onload: function(response) {
					// Check for 429 Rate Limit error
					if (response.status === 429) {
						if (retries > 0) {
							const newDelay = currentDelay * 1; // Exponential backoff
							console.log(`[DEBUG] Rate limited. Waiting ${newDelay / 1000} seconds before retrying...`);
							setTimeout(() => {
								resolve(fetchServersWithRetry(url, retries - 1, newDelay)); // Retry with increased delay
							}, newDelay);
						} else {
							console.error('[DEBUG] Rate limit retries exhausted.');
                            notifications('Error: Rate limited please try again later.', 'error', '⚠️')
							reject(new Error('RateLimit'));
						}
						return;
					}

					// Handle other HTTP errors
					if (response.status < 200 || response.status >= 300) {
						console.error('[DEBUG] HTTP error:', response.status, response.statusText);
						reject(new Error(`HTTP error: ${response.status}`));
						return;
					}

					// Parse and return the JSON data
					try {
						const data = JSON.parse(response.responseText);
						console.log('[DEBUG] Fetched data successfully:', data);
						resolve(data);
					} catch (error) {
						console.error('[DEBUG] Error parsing JSON:', error);
						reject(error);
					}
				},
				onerror: function(error) {
					console.error('[DEBUG] Error in GM_xmlhttpRequest:', error);
					reject(error);
				}
			});
		});
	}

	/*******************************************************
	name of function: filterServersByPlayerCount
	description: Filters servers to show only those with a player count equal to or below the specified max.
	If no exact matches are found, prioritizes servers with player counts lower than the input.
	Keeps fetching until at least 8 servers are found, with a dynamic delay between requests.
	*******************************************************/
	async function filterServersByPlayerCount(maxPlayers) {
		// Validate maxPlayers before proceeding
		if (isNaN(maxPlayers) || maxPlayers < 1 || !Number.isInteger(maxPlayers)) {
			console.error('[DEBUG] Invalid input for maxPlayers.');
			notifications('Error: Please input a valid whole number greater than or equal to 1.', 'error', '⚠️');
			return;
		}

		// Disable UI elements and clear the server list
		Loadingbar(true);
		disableLoadMoreButton();
		disableFilterButton(true);
		const serverList = document.querySelector('#rbx-game-server-item-container');
		serverList.innerHTML = '';

		const gameId = window.location.pathname.split('/')[2];
		let cursor = null;
		let serversFound = 0;
		let serverMaxPlayers = null;
		let isCloserToOne = null;
		let topDownServers = []; // Servers collected during top-down search
		let bottomUpServers = []; // Servers collected during bottom-up search
		let currentDelay = 500; // Initial delay of 0.5 seconds
		const timeLimit = 3 * 60 * 1000; // 3 minutes in milliseconds
		const startTime = Date.now(); // Record the start time
		notifications('Will search for a maximum of 3 minutes to find a server.', 'success', '🔎');


		try {
			while (serversFound < 16) {
				// Check if the time limit has been exceeded
				if (Date.now() - startTime > timeLimit) {
					console.log('[DEBUG] Time limit reached. Proceeding to fallback servers.');
					notifications('Warning: Time limit reached. Proceeding to fallback servers.', 'warning', '❗');
					break;
				}

				// Fetch initial data to determine serverMaxPlayers and isCloserToOne
				if (!serverMaxPlayers) {
					const initialUrl = cursor ?
						`https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=100&cursor=${cursor}` :
						`https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=100`;

					const initialData = await fetchServersWithRetry(initialUrl);
					if (initialData.data.length > 0) {
						serverMaxPlayers = initialData.data[0].maxPlayers;
						isCloserToOne = maxPlayers <= (serverMaxPlayers / 2);
					} else {
						console.error('[DEBUG] No servers found in initial fetch.');
						break;
					}
				}

				// Validate maxPlayers against serverMaxPlayers
				if (maxPlayers >= serverMaxPlayers) {
					console.error('[DEBUG] Invalid input: maxPlayers is greater than or equal to serverMaxPlayers.');
					notifications(`Error: Please input a number between 1 through ${serverMaxPlayers - 1}`, 'error', '⚠️');
					return;
				}

				// Adjust the URL based on isCloserToOne
				const baseUrl = isCloserToOne ?
					`https://games.roblox.com/v1/games/${gameId}/servers/public?sortOrder=1&excludeFullGames=true&limit=100` :
					`https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=100`; // why does this work lmao

				const url = cursor ? `${baseUrl}&cursor=${cursor}` : baseUrl;
				const data = await fetchServersWithRetry(url);

				// Safety check: Ensure the server list is valid and iterable
				if (!Array.isArray(data.data)) {
					console.error('[DEBUG] Invalid server list received. Waiting 1 second before retrying...');
					await delay(1000); // Wait 1 second before retrying
					continue; // Skip the rest of the loop and retry
				}

				// Filter and process servers
				for (const server of data.data) {
					if (server.playing === maxPlayers) {
						await rbx_card(server.id, server.playerTokens, server.maxPlayers, server.playing, gameId);
						serversFound++;

						if (serversFound >= 16) {
							break;
						}
					} else if (!isCloserToOne && server.playing > maxPlayers) {
						topDownServers.push(server); // Add to top-down fallback list
					} else if (isCloserToOne && server.playing < maxPlayers) {
						bottomUpServers.push(server); // Add to bottom-up fallback list
					}
				}

				// Exit if no more servers are available
				if (!data.nextPageCursor) {
					break;
				}

				cursor = data.nextPageCursor;

				// Adjust delay dynamically
				if (currentDelay > 150) {
					currentDelay = Math.max(150, currentDelay / 2); // Gradually reduce delay
				}
				console.log(`[DEBUG] Waiting ${currentDelay / 1000} seconds before next request...`);
				await delay(currentDelay);
			}

			// If no exact matches were found or time limit reached, use fallback servers
			if (serversFound === 0 && (topDownServers.length > 0 || bottomUpServers.length > 0)) {
				// Sort top-down servers by player count (ascending)
				topDownServers.sort((a, b) => a.playing - b.playing);

				// Sort bottom-up servers by player count (descending)
				bottomUpServers.sort((a, b) => b.playing - a.playing);

				// Combine both fallback lists (prioritize top-down servers first)
				const combinedFallback = [...topDownServers, ...bottomUpServers];

				for (const server of combinedFallback) {
					await rbx_card(server.id, server.playerTokens, server.maxPlayers, server.playing, gameId);
					serversFound++;

					if (serversFound >= 16) {
						break;
					}
				}
			}

			if (serversFound <= 0) {
				notifications('No Servers Found Within The Provided Criteria', 'info', '🔎');
			}
		} catch (error) {
			console.error('[DEBUG] Error in filterServersByPlayerCount:', error);
		} finally {
			Loadingbar(false);
			disableFilterButton(false);
		}
	}

	/*********************************************************************************************************************************************************************************************************************************************
	                                                         Functions for the 4th button

	*********************************************************************************************************************************************************************************************************************************************/

	/*******************************************************
	name of function: random_servers
	description: Fetches servers from two different URLs, combines the results, ensures no duplicates, shuffles the list, and passes the server information to the rbx_card function in a random order. Handles 429 errors with retries.
	*******************************************************/
	async function random_servers() {
		// Disable the "Load More" button and show the loading bar
		Loadingbar(true);
		disableFilterButton(true);
		disableLoadMoreButton();

		// Get the game ID from the URL
		const gameId = window.location.pathname.split('/')[2];

		try {
			// Fetch servers from the first URL with retry logic
			const firstUrl = `https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=10`;
			const firstData = await fetchWithRetry(firstUrl, 3); // Retry up to 3 times

			// Wait for 5 seconds
			await delay(5000);

			// Fetch servers from the second URL with retry logic
			const secondUrl = `https://games.roblox.com/v1/games/${gameId}/servers/public?sortOrder=1&excludeFullGames=true&limit=10`;
			const secondData = await fetchWithRetry(secondUrl, 3); // Retry up to 3 times

			// Combine the servers from both URLs
			const combinedServers = [...firstData.data, ...secondData.data];

			// Remove duplicates by server ID
			const uniqueServers = [];
			const seenServerIds = new Set();

			for (const server of combinedServers) {
				if (!seenServerIds.has(server.id)) {
					seenServerIds.add(server.id);
					uniqueServers.push(server);
				}
			}

			// Shuffle the unique servers array
			const shuffledServers = shuffleArray(uniqueServers);

			// Get the first 16 shuffled servers
			const selectedServers = shuffledServers.slice(0, 16);

			// Process each server in random order
			for (const server of selectedServers) {
				const {
					id: serverId,
					playerTokens,
					maxPlayers,
					playing
				} = server;

				// Pass the server data to the card creation function
				await rbx_card(serverId, playerTokens, maxPlayers, playing, gameId);
			}
		} catch (error) {
			console.error('Error fetching server data:', error);
			notifications('Error: Failed to fetch server data. Please try again later.', 'error', '⚠️');
		} finally {
			// Hide the loading bar and enable the filter button
			Loadingbar(false);
			disableFilterButton(false);
		}
	}

	/*******************************************************
	name of function: fetchWithRetry
	description: Fetches data from a URL with retry logic for 429 errors. this is for this unique function
	*******************************************************/
	async function fetchWithRetry(url, retries) {
		for (let i = 0; i < retries; i++) {
			try {
				const response = await fetch(url);
				if (response.status === 429) {
					// If 429 error, wait 10 seconds and retry
					console.log(`Rate limited. Retrying in 10 seconds... (Attempt ${i + 1}/${retries})`);
					await delay(10000); // Wait 10 seconds
					continue;
				}
				if (!response.ok) {
					throw new Error(`HTTP error: ${response.status}`);
				}
				return await response.json();
			} catch (error) {
				if (i === retries - 1) {
					// If no retries left, throw the error
					throw error;
				}
			}
		}
	}

	/*******************************************************
	name of function: shuffleArray
	description: Shuffles an array using the Fisher-Yates algorithm.
	*******************************************************/
	function shuffleArray(array) {
		for (let i = array.length - 1; i > 0; i--) {
			const j = Math.floor(Math.random() * (i + 1)); // Random index from 0 to i
			[array[i], array[j]] = [array[j], array[i]]; // Swap elements
		}
		return array;
	}


	/*********************************************************************************************************************************************************************************************************************************************
	                                                         Functions for the 5th button. taken from my other project

	*********************************************************************************************************************************************************************************************************************************************/

	// so we inject css into the page. if ur on light mode some stuff may look weird so not my fault
	const style = document.createElement('style');
	style.textContent = `
/* Overlay for the stupid thingy black screen*/
.overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.7); /* Dark semi-transparent background */
    z-index: 1000; /* Ensure overlay is below the popup */
}

/* Popup Container for the server region*/
.filter-popup {
    background-color: #2d2d2d; /* Dark background */
    color: #ffffff; /* White text */
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
    width: 300px;
    max-width: 90%;
    position: fixed; /* Fixed positioning */
    top: 50%; /* Center vertically */
    left: 50%; /* Center horizontally */
    transform: translate(-50%, -50%); /* Offset to truly center */
    text-align: center;
    z-index: 1001; /* Ensure popup is above the overlay */
}

/* Close Button  for the server selector*/
#closePopup {
    position: absolute;
    top: 10px;
    right: 10px;
    background: #ff4444; /* Red background */
    border: none;
    color: white;
    font-size: 16px;
    cursor: pointer;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

#closePopup:hover {
    background: #cc0000; /* Darker red on hover */
}

/* Label */
.filter-popup label {
    display: block;
    margin-bottom: 10px;
    font-size: 16px;
    color: #ffffff;
}

/* Dropdown */
.filter-popup select {
    background-color: #444; /* Dark gray background */
    color: #ffffff; /* White text */
    padding: 8px;
    border-radius: 5px;
    border: 1px solid #666; /* Gray border */
    width: 100%;
    margin-bottom: 10px;
    font-size: 14px;
}

.filter-popup select:focus {
    border-color: #888; /* Lighter border on focus */
    outline: none;
}

/* Custom Input */
.filter-popup input[type="number"] {
    background-color: #444; /* Dark gray background */
    color: #ffffff; /* White text */
    padding: 8px;
    border-radius: 5px;
    border: 1px solid #666; /* Gray border */
    width: 100%;
    margin-bottom: 10px;
    font-size: 14px;
}

.filter-popup input[type="number"]:focus {
    border-color: #888; /* Lighter border on focus */
    outline: none;
}

/* Confirm Button */
#confirmServerCount {
    background-color: #444; /* Dark gray background */
    color: #ffffff; /* White text */
    padding: 8px 16px;
    border: 1px solid #666; /* Gray border */
    border-radius: 5px;
    cursor: pointer;
    font-size: 14px;
    width: 100%;
    transition: background-color 0.3s ease;
}

#confirmServerCount:hover {
    background-color: #666; /* Lighter gray on hover */
}
        .rbx-game-server-item.highlighted {
            border: 2px solid green;
            border-radius: 8px;
        }
        .fetch-button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }
    `;
	document.head.appendChild(style);


	// Function to show the message under the "Load More" button
	function showMessage(message) {
		const loadMoreButtonContainer = document.querySelector('.rbx-running-games-footer');

		if (!loadMoreButtonContainer) {
			console.error("Load More button container not found!");
			return;
		}

		// Create the message element
		const messageElement = document.createElement('div');
		messageElement.className = 'filter-message';
		messageElement.textContent = message;

		// Clear any existing message and append the new one
		const existingMessage = loadMoreButtonContainer.querySelector('.filter-message');
		if (existingMessage) {
			existingMessage.remove(); // Remove the existing message if it exists
		}

		loadMoreButtonContainer.appendChild(messageElement);

		return messageElement;
	}

	// Function to hide the message of the showmessage functioon
	function hideMessage() {
		const messageElement = document.querySelector('.filter-message');
		if (messageElement) messageElement.remove();
	}

	// Function to show the popup for random stuff
	function showPopup() {
		const overlay = document.createElement('div');
		overlay.className = 'overlay';

		const popup = document.createElement('div');
		popup.className = 'filter-popup';
		popup.textContent = 'Filtering servers, please wait...';

		document.body.appendChild(overlay);
		document.body.appendChild(popup);

		return popup;
	}

	// Function to hide the popup for the stuff
	function hidePopup() {
		const popup = document.querySelector('.filter-popup');
		const overlay = document.querySelector('.overlay');

		if (popup) popup.remove();
		if (overlay) overlay.remove();
	}

	// Function to fetch server details so game id and job id. yea!
	async function fetchServerDetails(gameId, jobId) {
		return new Promise((resolve, reject) => {
			GM_xmlhttpRequest({
				method: "POST",
				url: "https://gamejoin.roblox.com/v1/join-game-instance", // url for game id
				headers: { // doesent need cookie cuase of magic
					"Content-Type": "application/json",
					"User-Agent": "Roblox/WinInet",
				},
				data: JSON.stringify({
					placeId: gameId,
					gameId: jobId
				}),
				onload: function(response) {
					const json = JSON.parse(response.responseText);

					console.log("API Response:", json); // This prints the full response

					// Check if the response indicates that the user needs to purchase the game
					if (json.status === 12 && json.message === 'You need to purchase access to this game before you can play.') { // yea error message!
						reject('purchase_required'); // Special error code for this case yea!
						return;
					}

					const address = json?.joinScript?.UdmuxEndpoints?.[0]?.Address ?? json?.joinScript?.MachineAddress;

					if (!address) {
						console.error("API Response (Unknown Location) Which means Full Server!:", json); // Log the API response for debug
						reject(`Unable to fetch server location: Status ${json.status}`); // debug
						return;
					}

					const location = serverRegionsByIp[address.replace(/^(128\.116\.\d+)\.\d+$/, "$1.0")]; // lmao all servers atart with this so yea dont argue with me

					if (!location) {
						console.error("API Response (Unknown Location):", json); // Log the API response into the chat. might remove it from production but idc rn
						reject(`Unknown server address ${address}`);
						return;
					}

					resolve(location);
				},
				onerror: function(error) {
					console.error("API Request Failed:", error); // damn if this happpens idk what to tell u
					reject(`Failed to fetch server details: ${error}`);
				},
			});
		});
	}

	// cusomt delay also known as sleep fucntion in js cause this language sucks and doesent have a default function
	function delay(ms) {
		return new Promise(resolve => setTimeout(resolve, ms));
	}

	// Function to create a popup for selecting the number of servers
	// basically yea thats what it doesent
	function createServerCountPopup(callback) {
		const overlay = document.createElement('div');
		overlay.className = 'overlay';

		const popup = document.createElement('div');
		popup.className = 'filter-popup'; // reason 100 is selected because thjats how many the api will show per request
		popup.innerHTML = `
        <button id="closePopup">X</button>
        <label for="serverCount">Enter the number of servers to search (higher values yield more location variety).</label>
        <select id="serverCount">
            <option value="10">10 Servers</option>
            <option value="25">25 Servers</option>
            <option value="50">50 Servers</option>
            <option value="100" selected>100 Servers</option>
            <option value="200">200 Servers</option>
            <option value="500">500 Servers</option>
            <option value="1000">1000 Servers</option>
            <option value="custom">Custom</option>
        </select>
        <input id="customServerCount" type="number" min="1" max="1000" placeholder="Enter a number (1-1000)" style="display: none;">
        <button id="confirmServerCount">Confirm</button>
    `;

		document.body.appendChild(overlay);
		document.body.appendChild(popup);

		const serverCountDropdown = popup.querySelector('#serverCount');
		const customServerCountInput = popup.querySelector('#customServerCount');
		const confirmButton = popup.querySelector('#confirmServerCount');
		const closeButton = popup.querySelector('#closePopup');

		// Show/hide custom input based on dropdown selection
		serverCountDropdown.addEventListener('change', () => {
			if (serverCountDropdown.value === 'custom') {
				customServerCountInput.style.display = 'block';
			} else {
				customServerCountInput.style.display = 'none';
			}
		});

		// button click on start or what ever
		confirmButton.addEventListener('click', () => {
			let serverCount;

			if (serverCountDropdown.value === 'custom') {
				serverCount = parseInt(customServerCountInput.value);

				// Validate custom input
				if (isNaN(serverCount) || serverCount < 1 || serverCount > 1000) {
					notifications('Error: Please enter a valid number between 1 and 1000.', 'error', '⚠️')
					return;
				}
			} else {
				serverCount = parseInt(serverCountDropdown.value);
			}

			// Show an alert if the user selects a number above 100
			if (serverCount > 100) { // error cause people dont know about this maybe. idk yea so here. also if u think this is a stupid way i should have done it before the button press idc so yea
				notifications('Warning: Searching over 100 servers may take some time and you might get rate limited!', 'warning', '❗');
			}

			// Pass the selected server count to the callback
			callback(serverCount);
			disableFilterButton(true); // disbale filter button
			disableLoadMoreButton(true); // disable load more button
			notifications('Note: Filter Button is disabled as this function is resource intensive. \nRefresh the page to call other functions/press other buttons.', 'info', '⚠️')
			hidePopup();
			Loadingbar(true); // enable loading bar
		});

		// Close button logic :))
		closeButton.addEventListener('click', () => {
			hidePopup();
		});

		// Function to hide the popup
		// yea im dumb and used the same function name but it works and im too lazy to change it
		function hidePopup() {
			document.body.removeChild(overlay);
			document.body.removeChild(popup);
		}
	}

	// Function to fetch public servers
	// totallimit is amount of sevrers to fetch
	async function fetchPublicServers(gameId, totalLimit) {
		let servers = [];
		let cursor = null;

		while (servers.length < totalLimit) { // too lazy to comment any of this. hopefully i remember what this does in the future
			const url = `https://games.roblox.com/v1/games/${gameId}/servers/public?excludeFullGames=true&limit=100${cursor ? `&cursor=${cursor}` : ''}`;

			const response = await new Promise((resolve, reject) => {
				GM_xmlhttpRequest({
					method: "GET",
					url: url,
					onload: function(response) {
						resolve(JSON.parse(response.responseText));
					},
					onerror: function(error) {
						reject(`Failed to fetch public servers: ${error}`);
					},
				});
			});

			servers = servers.concat(response.data);

			if (!response.nextPageCursor || servers.length >= totalLimit) {
				break;
			}

			cursor = response.nextPageCursor;
			await delay(3000); // wait 3 seconds before each page request. if u think this is slow i tried 1 second i got rate limited :|
		}

		return servers.slice(0, totalLimit);
	}

	// Function to create dropdown menus for filtering
	function createFilterDropdowns(servers) {
		const filterContainer = document.createElement('div');
		filterContainer.className = 'filter-container';

		const countryDropdown = document.createElement('select');
		countryDropdown.id = 'countryFilter';
		countryDropdown.innerHTML = '<option value="">All Countries</option>';
		countryDropdown.style.backgroundColor = '#333'; // Dark gray background
		countryDropdown.style.color = '#fff'; // White text
		countryDropdown.style.borderRadius = '8px'; // Rounded corners
		countryDropdown.style.padding = '8px'; // Increase size
		countryDropdown.style.fontSize = '16px'; // Increase font size
		countryDropdown.style.border = 'none'; // Remove default border

		const cityDropdown = document.createElement('select');
		cityDropdown.id = 'cityFilter';
		cityDropdown.innerHTML = '<option value="">All Cities</option>';
		cityDropdown.style.backgroundColor = '#333'; // Dark gray background
		cityDropdown.style.color = '#fff'; // White text
		cityDropdown.style.borderRadius = '8px'; // Rounded corners
		cityDropdown.style.padding = '8px'; // Increase size hehehehe
		cityDropdown.style.fontSize = '16px'; // Increase font size
		cityDropdown.style.border = 'none'; // Remove default border
		cityDropdown.style.marginLeft = '5px'; // move right cause im too lazy to fix

		// Count the number of servers per country and add them to the dropdown
		const countryCounts = {};
		servers.forEach(server => {
			const country = server.location.country.name;
			countryCounts[country] = (countryCounts[country] || 0) + 1;
		});

		// Populate country dropdown with server counts
		Object.keys(countryCounts).forEach(country => {
			const option = document.createElement('option');
			option.value = country;
			option.textContent = `${country} (${countryCounts[country]})`;
			countryDropdown.appendChild(option);
		});

		// add the city dropdown based on selected country
		countryDropdown.addEventListener('change', () => {
			const selectedCountry = countryDropdown.value;
			cityDropdown.innerHTML = '<option value="">All Cities</option>';

			if (selectedCountry) {
				// Count the number of servers per city in the selected country
				const cityCounts = {};
				servers
					.filter(server => server.location.country.name === selectedCountry)
					.forEach(server => {
						const city = server.location.city;
						const region = server.location.region?.name;
						const cityKey = region ? `${city}, ${region}` : city;
						cityCounts[cityKey] = (cityCounts[cityKey] || 0) + 1;
					});

				// Populate city dropdown with server counts
				Object.keys(cityCounts).forEach(city => {
					const option = document.createElement('option');
					option.value = city;
					option.textContent = `${city} (${cityCounts[city]})`;
					cityDropdown.appendChild(option);
				});

				// Auto-select the city if there's only one make users life easier
				// wow ik i made the users life easier for once thats crazy!!! :OOOOO
				const cities = Object.keys(cityCounts);
				if (cities.length === 1) {
					cityDropdown.value = cities[0];
					// displayFilteredServers(selectedCountry, cities[0]); // if this breaks something which it doesent seem like it i will enable it later
				}
			}
		});

		filterContainer.appendChild(countryDropdown);
		filterContainer.appendChild(cityDropdown);

		return filterContainer;
	}

	// Function to filter servers based on selected country and city cause im lazy
	function filterServers(servers, country, city) {
		return servers.filter(server => {
			const matchesCountry = !country || server.location.country.name === country;
			const matchesCity = !city || `${server.location.city}${server.location.region?.name ? `, ${server.location.region.name}` : ''}` === city;
			return matchesCountry && matchesCity;
		});
	}

	// Function to sort servers by ping. maybe inaccurate but thats roblox's problem not mine
	function sortServersByPing(servers) {
		return servers.sort((a, b) => a.server.ping - b.server.ping);
	}

	async function fetchPlayerThumbnails_servers(playerTokens) {
		const body = playerTokens.map(token => ({
			requestId: `0:${token}:AvatarHeadshot:150x150:png:regular`,
			type: "AvatarHeadShot",
			targetId: 0,
			token,
			format: "png",
			size: "150x150",
		}));

		const response = await fetch("https://thumbnails.roblox.com/v1/batch", {
			method: "POST",
			headers: {
				"Content-Type": "application/json",
				Accept: "application/json",
			},
			body: JSON.stringify(body),
		});

		const data = await response.json();
		return data.data || [];
	}

	async function rebuildServerList(gameId, totalLimit, best_connection) {
		const serverListContainer = document.getElementById("rbx-game-server-item-container");


		// If "Best Connection" is enabled
		// FUNCTION FOR THE 6TH BUTTON!
		if (best_connection === true) {
			// Ask for the user's location
			const userLocation = await getUserLocation();
			if (!userLocation) {
				//notifications('Error: Unable to fetch your location. Please enable location access.', 'error', '⚠️');
				return;
			}

			// Fetch 50 servers
			const servers = await fetchPublicServers(gameId, 50);
			if (servers.length === 0) {
				notifications('Error: No servers found. Please try again later.', 'error', '⚠️');
				return;
			}

			// Calculate distances and find the closest server
			let closestServer = null;
			let minDistance = Infinity;
			let closestServerLocation = null;

			for (const server of servers) {
				const {
					id: serverId,
					maxPlayers,
					playing
				} = server;

				// Skip full servers
				if (playing >= maxPlayers) {
					continue;
				}

				try {
					// Fetch server location
					const location = await fetchServerDetails(gameId, serverId);

					// Calculate distance
					const distance = calculateDistance(
						userLocation.latitude,
						userLocation.longitude,
						location.latitude,
						location.longitude
					);

					// Update closest server
					if (distance < minDistance) {
						minDistance = distance;
						closestServer = server;
						closestServerLocation = location;
					}
				} catch (error) {
					console.error(`Error fetching details for server ${serverId}:`, error);
					// Skip this server and continue with the next one
					continue;
				}
			}

			if (closestServer) {
				// Automatically join the closest server
				Roblox.GameLauncher.joinGameInstance(gameId, closestServer.id);
				notifications(`Joining nearest server!
            Server ID: ${closestServer.id}
            Distance: ${(minDistance / 1.609).toFixed(2)} miles | ${minDistance.toFixed(2)} km
            Location (Country): ${closestServerLocation.country.name}.`, 'success', '🚀');

				disableFilterButton(false);
				Loadingbar(false);
			} else {
				notifications('No valid servers found. Please try again later after refreshing the webpage.', 'error', '⚠️');
			}

			return; // Exit the function after joining the best server
		}

		// Rest of the original function (for non-"Best Connection" mode)
		if (!serverListContainer) {
			console.error("Server list container not found!");
			const popup = showPopup();
			notifications('Error: No Servers found. There is nobody playing this game. :(', 'warning', '❗');
			return;
		}

		const messageElement = showMessage("Filtering servers, please wait...");

		try {
			const servers = await fetchPublicServers(gameId, totalLimit);
			const totalServers = servers.length;
			let skippedServers = 0;

			messageElement.textContent = `Filtering servers, please do not leave this page as it slows down the search...\n${totalServers} servers found, 0 servers loaded.`;
			notifications(`Please do not leave this page as it slows down the search. \nFound a total of ${totalServers} servers found.`, 'success', '👍');

			const serverDetails = [];
			for (let i = 0; i < servers.length; i++) {
				const server = servers[i];
				const {
					id: serverId,
					maxPlayers,
					playing,
					ping,
					fps,
					playerTokens
				} = server;

				let location;
				try {
					location = await fetchServerDetails(gameId, serverId);
				} catch (error) {
					if (error === 'purchase_required') {
						messageElement.textContent = "Cannot access server data because you haven't purchased the game.";
						notifications('Error: Cannot access server data because you haven\'t purchased the game.', 'error', '⚠️');
						Loadingbar(false); // disable loading bar
						return;
					} else {
						console.error(error);
						location = {
							city: "Unknown",
							country: {
								name: "Unknown",
								code: "??"
							}
						};
					}
				}

				if (location.city === "Unknown" || playing >= maxPlayers) {
					console.log(`Skipping server ${serverId} because it is full or location is unknown.`);
					skippedServers++;
					continue;
				}

				// Fetch player thumbnails
				const playerThumbnails = playerTokens && playerTokens.length > 0 ? await fetchPlayerThumbnails_servers(playerTokens) : [];

				serverDetails.push({
					server,
					location,
					playerThumbnails
				});

				messageElement.textContent = `Filtering servers, please do not leave this page...\n${totalServers} servers found, ${i + 1} server locations found`;
			}

			if (serverDetails.length === 0) {
				messageElement.textContent = "No servers found. Please try again with an increase in the number of servers to search for.";
				notifications('Error: No servers found. Please try again with an increase in the number of servers to search for.', 'error', '⚠️');
				Loadingbar(false); // disable loading bar
				return;
			}

			const loadedServers = totalServers - skippedServers;
			notifications(`Filtering complete!\n${totalServers} servers found, ${loadedServers} servers loaded, ${skippedServers} servers skipped (full).`, 'success', '👍');
			messageElement.textContent = `Filtering complete!\n${totalServers} servers found, ${loadedServers} servers loaded, ${skippedServers} servers skipped (full).`;
			Loadingbar(false); // disable loading bar

			// Add filter dropdowns
			const filterContainer = createFilterDropdowns(serverDetails);
			serverListContainer.parentNode.insertBefore(filterContainer, serverListContainer);

			// Style the server list container to use a grid layout
			serverListContainer.style.display = "grid";
			serverListContainer.style.gridTemplateColumns = "repeat(4, 1fr)"; // 4 columns
			serverListContainer.style.gap = "16px"; // Gap between cards

			const displayFilteredServers = (country, city) => {
				serverListContainer.innerHTML = "";

				const filteredServers = filterServers(serverDetails, country, city);
				const sortedServers = sortServersByPing(filteredServers);

				sortedServers.forEach(({
					server,
					location,
					playerThumbnails
				}) => {
					const serverCard = document.createElement("li");
					serverCard.className = "rbx-game-server-item col-md-3 col-sm-4 col-xs-6";

					// Set consistent width and height for the server card
					serverCard.style.width = "100%"; // Take up full width of the grid cell
					serverCard.style.minHeight = "400px"; // Set a minimum height
					serverCard.style.display = "flex";
					serverCard.style.flexDirection = "column";
					serverCard.style.justifyContent = "space-between";
					serverCard.style.boxSizing = "border-box"; // Include padding and border in dimensions

					// Remove any conflicting outline (e.g., from .highlighted class)
					serverCard.style.outline = 'none';

					// Determine the group and set the outline color
					let outlineColor;
					if (server.ping < 100) {
						outlineColor = 'green'; // Best ping
					} else if (server.ping < 200) {
						outlineColor = 'orange'; // Medium ping
					} else {
						outlineColor = 'red'; // Bad ping
					}

					// Apply the new outline and outlineOffset
					serverCard.style.outline = `3px solid ${outlineColor}`;
					serverCard.style.outlineOffset = '-6px';
					serverCard.style.padding = '6px';
					serverCard.style.borderRadius = '8px';

					// Create a container for player thumbnails
					const thumbnailsContainer = document.createElement("div");
					thumbnailsContainer.className = "player-thumbnails-container";
					thumbnailsContainer.style.display = "grid";
					thumbnailsContainer.style.gridTemplateColumns = "repeat(3, 60px)"; // 3 columns
					thumbnailsContainer.style.gridTemplateRows = "repeat(2, 60px)"; // 2 rows
					thumbnailsContainer.style.gap = "5px";
					thumbnailsContainer.style.marginBottom = "10px";

					// Add player thumbnails to the container (max 5)
					const maxThumbnails = 5;
					const displayedThumbnails = playerThumbnails.slice(0, maxThumbnails);
					displayedThumbnails.forEach(thumb => {
						if (thumb && thumb.imageUrl) {
							const img = document.createElement("img");
							img.src = thumb.imageUrl;
							img.className = "avatar-card-image";
							img.style.width = "60px";
							img.style.height = "60px";
							img.style.borderRadius = "50%";
							thumbnailsContainer.appendChild(img);
						}
					});

					// Add a placeholder for hidden players
					const hiddenPlayers = server.playing - displayedThumbnails.length;
					if (hiddenPlayers > 0) {
						const placeholder = document.createElement("div");
						placeholder.className = "avatar-card-image";
						placeholder.style.width = "60px";
						placeholder.style.height = "60px";
						placeholder.style.borderRadius = "50%";
						placeholder.style.backgroundColor = "#BDBEBE80"; // Dark gray background
						placeholder.style.display = "flex";
						placeholder.style.alignItems = "center";
						placeholder.style.justifyContent = "center";
						placeholder.style.color = "#fff"; // White text
						placeholder.style.fontSize = "14px";
						placeholder.textContent = `+${hiddenPlayers}`;
						thumbnailsContainer.appendChild(placeholder);
					}

					// Server card content
					const cardItem = document.createElement("div");
					cardItem.className = "card-item";
					cardItem.style.display = "flex";
					cardItem.style.flexDirection = "column";
					cardItem.style.justifyContent = "space-between";
					cardItem.style.height = "100%"; // Ensure the card content takes up the full height

					cardItem.innerHTML = `
                <!-- Player thumbnails at the top -->
                ${thumbnailsContainer.outerHTML}
                <div class="rbx-game-server-details game-server-details">
                    <div class="text-info rbx-game-status rbx-game-server-status text-overflow">
                        ${server.playing} of ${server.maxPlayers} people max
                    </div>
                    <div class="server-player-count-gauge border">
                        <div class="gauge-inner-bar border" style="width: ${(server.playing / server.maxPlayers) * 100}%;"></div>
                    </div>
                    <span data-placeid="${gameId}">
                        <button type="button" class="btn-full-width btn-control-xs rbx-game-server-join game-server-join-btn btn-primary-md btn-min-width">Join</button>
                    </span>
                </div>
                <!-- Generated info (ping, location, FPS) at the bottom -->
                <div style="margin-top: 10px; text-align: center;">
                    <div class="ping-info">Ping: ${server.ping}ms</div>
                    <div class="location-info">${location.city}, ${location.country.name}</div>
                    <div class="fps-info">FPS: ${Math.round(server.fps)}</div>
                </div>
            `;

					const joinButton = cardItem.querySelector(".rbx-game-server-join");
					joinButton.addEventListener("click", () => {
						console.log(`Roblox.GameLauncher.joinGameInstance(${gameId}, "${server.id}")`);
						Roblox.GameLauncher.joinGameInstance(gameId, server.id); // join server
					});

					const container = adjustJoinButtonContainer(joinButton);
					const inviteButton = createInviteButton(gameId, server.id);
					container.appendChild(inviteButton);

					serverCard.appendChild(cardItem);
					serverListContainer.appendChild(serverCard);
				});
			};

			// Add event listeners to dropdowns
			const countryFilter = document.getElementById('countryFilter');
			const cityFilter = document.getElementById('cityFilter');

			countryFilter.addEventListener('change', () => {
				displayFilteredServers(countryFilter.value, cityFilter.value);
			});

			cityFilter.addEventListener('change', () => {
				displayFilteredServers(countryFilter.value, cityFilter.value);
			});

			// Display all servers initially
			displayFilteredServers("", "");

			setTimeout(() => {
				hideMessage();
			}, 3000);
		} catch (error) {
			console.error("Error rebuilding server list:", error);
			notifications('An error occurred while filtering servers. Please try again.', 'error', '😔');
			messageElement.textContent = "An error occurred while filtering servers. Please try again.";
			Loadingbar(false); // enable loading bar
		} finally {
			Loadingbar(false); // omg bruh i just realzed i could put this here but now im too lazy to thorugh the code to remove all of the loading bar disabl functions
		}
	}

	// Function to extract the game ID from the URL
	function extractGameId() {
		const url = window.location.href;
		const match = url.match(/roblox\.com\/games\/(\d+)/);

		if (match && match[1]) {
			return match[1]; // Return the game ID
		}
		return null; // Return null if no game ID is found
	}

	// Log the game ID to the console
	const gameId = extractGameId();

	// Function to create and append the Invite button
	function createInviteButton(placeId, serverId) { // too lazy to comment this function tbh just ready the name
		const inviteButton = document.createElement('button');
		inviteButton.textContent = 'Invite';
		inviteButton.className = 'btn-control-xs btn-primary-md btn-min-width btn-full-width';
		inviteButton.style.width = '25%';
		inviteButton.style.marginLeft = '5px';

		inviteButton.style.padding = '4px 8px';
		inviteButton.style.fontSize = '12px';
		inviteButton.style.borderRadius = '8px';
		inviteButton.style.backgroundColor = '#393b3d';
		inviteButton.style.borderColor = '#bdbebe';
		inviteButton.style.color = '#bdbebe';
		inviteButton.style.cursor = 'pointer';
		inviteButton.style.fontWeight = '500';
		inviteButton.style.textAlign = 'center';
		inviteButton.style.whiteSpace = 'nowrap';
		inviteButton.style.verticalAlign = 'middle';
		inviteButton.style.lineHeight = '100%';
		inviteButton.style.fontFamily = 'Builder Sans, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif';
		inviteButton.style.textRendering = 'auto';
		inviteButton.style.webkitFontSmoothing = 'antialiased';
		inviteButton.style.mozOsxFontSmoothing = 'grayscale';

		inviteButton.addEventListener('mouseenter', () => {
			inviteButton.style.color = '#ffffff';
			inviteButton.style.borderColor = '#ffffff';
		});
		inviteButton.addEventListener('mouseleave', () => {
			inviteButton.style.color = '#bdbebe';
			inviteButton.style.borderColor = '#bdbebe';
		});

		inviteButton.addEventListener('click', () => {
			const inviteLink = `https://oqarshi.github.io/Invite/?placeid=${placeId}&serverid=${serverId}`;
			navigator.clipboard.writeText(inviteLink).then(() => {
				console.log(`Invite link copied to clipboard: ${inviteLink}`);
				notifications('Success! Invite link copied to clipboard!', 'success', '🎉');
			}).catch(() => {
				console.error('Failed to copy invite link.');
				notifications('Error: Failed to copy invite link', 'error', '😔');
			});
		});

		return inviteButton;
	}

	// Function to adjust the Join button and its container
	function adjustJoinButtonContainer(joinButton) {
		const container = document.createElement('div');
		container.style.display = 'flex';
		container.style.width = '100%';

		joinButton.style.width = '75%';

		joinButton.parentNode.insertBefore(container, joinButton);
		container.appendChild(joinButton);

		return container;
	}




	/*********************************************************************************************************************************************************************************************************************************************
	                                                         Functions for the 6th button.

	*********************************************************************************************************************************************************************************************************************************************/




	function calculateDistance(lat1, lon1, lat2, lon2) {
		const R = 6371; // Radius of the Earth in kilometers
		const dLat = (lat2 - lat1) * (Math.PI / 180);
		const dLon = (lon2 - lon1) * (Math.PI / 180);
		const a =
			Math.sin(dLat / 2) * Math.sin(dLat / 2) +
			Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) *
			Math.sin(dLon / 2) * Math.sin(dLon / 2);
		const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
		return R * c; // Distance in kilometers
	}


	function getUserLocation() {
		return new Promise((resolve, reject) => {
			if (navigator.geolocation) {
				navigator.geolocation.getCurrentPosition(
					(position) => {
						notifications('We successfully detected your location.\nConnecting you to a nearby server in about 15-20 seconds. 😊', 'success', '🌎');
						disableLoadMoreButton(true);
						disableFilterButton(true);
						Loadingbar(true);
						resolve({
							latitude: position.coords.latitude,
							longitude: position.coords.longitude,
						});
					},
					(error) => {
						console.error('Error getting user location:', error);
						disableLoadMoreButton(true);
						disableFilterButton(true);
						Loadingbar(true);
						notifications('Error getting user location.\nPlease enable location permissions for this website.\nAssuming your location is New York in the United States.', 'error', '⚠️');
						// Fallback to a default location (e.g., New York City)
						resolve({
							latitude: 40.7128, // Default latitude (New York City)
							longitude: -74.0060, // Default longitude (New York City)
						});
					}
				);
			} else {
				console.error('Geolocation is not supported by this browser.');
				disableLoadMoreButton(true);
				disableFilterButton(true);
				Loadingbar(true);
				notifications('Error getting user location.\nThis browser doesent support location.\nAssuming your location is New York in the United States.', 'error', '⚠️');
				// Fallback to a default location (e.g., New York City)
				resolve({
					latitude: 40.7128, // Default latitude (New York City)
					longitude: -74.0060, // Default longitude (New York City)
				});
			}
		});
	}



	/*********************************************************************************************************************************************************************************************************************************************
	                                                         Functions for the 7th button.

	*********************************************************************************************************************************************************************************************************************************************/
	async function auto_join_small_server() {
		// Disable the "Load More" button and show the loading bar
		Loadingbar(true);
		disableFilterButton(true);
		disableLoadMoreButton();

		// Get the game ID from the URL
		const gameId = window.location.pathname.split('/')[2];

		// Retry mechanism for 429 errors
		let retries = 3; // Number of retries
		let success = false;

		while (retries > 0 && !success) {
			try {
				// Fetch server data using GM_xmlhttpRequest
				const data = await new Promise((resolve, reject) => {
					GM_xmlhttpRequest({
						method: "GET",
						url: `https://games.roblox.com/v1/games/${gameId}/servers/public?sortOrder=1&excludeFullGames=true&limit=100`,
						onload: function(response) {
							if (response.status === 429) {
								reject('429: Too Many Requests');
							} else if (response.status >= 200 && response.status < 300) {
								resolve(JSON.parse(response.responseText));
							} else {
								reject(`HTTP error: ${response.status}`);
							}
						},
						onerror: function(error) {
							reject(error);
						},
					});
				});

				// Find the server with the lowest player count
				let minPlayers = Infinity;
				let targetServer = null;

				for (const server of data.data) {
					if (server.playing < minPlayers) {
						minPlayers = server.playing;
						targetServer = server;
					}
				}

				if (targetServer) {
					// Join the server with the lowest player count
					Roblox.GameLauncher.joinGameInstance(gameId, targetServer.id);
					notifications(`Joining a server with ${targetServer.playing} player(s).`, 'success', '🚀');
					success = true; // Mark as successful
				} else {
					notifications('No available servers found.', 'error', '⚠️');
					break; // Exit the loop if no servers are found
				}
			} catch (error) {
				if (error === '429: Too Many Requests' && retries > 0) {
					console.log('Rate limited. Retrying in 10 seconds...');
					notifications('Rate limited. Retrying in 10 seconds...', 'warning', '⏳');
					await delay(10000); // Wait 10 seconds before retrying
					retries--;
				} else {
					console.error('Error fetching server data:', error);
					notifications('Error: Failed to fetch server data. Please try again later.', 'error', '⚠️');
					break; // Exit the loop if it's not a 429 error or no retries left
				}
			}
		}

		// Hide the loading bar and enable the filter button
		Loadingbar(false);
		disableFilterButton(false);
	}


	/*********************************************************************************************************************************************************************************************************************************************
	                                                         Functions for the 8th button.

	*********************************************************************************************************************************************************************************************************************************************/

	function credits() {
		// Inject CSS for the popup
		const css = `
            .credits-popup {
                display: flex;
                position: fixed;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                background-color: rgba(0, 0, 0, 0.8);
                justify-content: center;
                align-items: center;
                z-index: 1000;
                opacity: 0;
                animation: fadeIn 0.5s ease-in-out forwards;
            }
            .credits-popup-content {
                background-color: #000;
                padding: 20px;
                border-radius: 10px;
                width: 300px;
                box-shadow: 0 5px 15px rgba(255, 255, 255, 0.1);
                text-align: center;
                position: relative;
                color: #fff;
            }
            .credits-popup-content h2 {
                margin-top: 0;
                color: #fff;
            }
            .credits-popup-content .version {
                font-size: 14px;
                color: #aaa;
                margin-bottom: 10px;
            }
            .credits-popup-content ul {
                list-style-type: none;
                padding: 0;
            }
            .credits-popup-content ul li {
                margin: 10px 0;
                color: #bbb;
            }
            .credits-popup-content a {
                color: #4da6ff;
                text-decoration: none;
            }
            .credits-popup-content a:hover {
                text-decoration: underline;
            }
            .credits-popup-close {
                position: absolute;
                top: 10px;
                right: 10px;
                font-size: 24px;
                font-weight: bold;
                cursor: pointer;
                color: #fff;
            }
            .credits-popup-close:hover {
                color: #ccc;
            }
            @keyframes fadeIn {
                from { opacity: 0; }
                to { opacity: 1; }
            }
            @keyframes fadeOut {
                from { opacity: 1; }
                to { opacity: 0; }
            }
        `;

		// Add CSS to the document
		const style = document.createElement('style');
		style.type = 'text/css';
		style.innerHTML = css;
		document.head.appendChild(style);

		// Create the popup HTML
		const popupHTML = `
            <div class="credits-popup">
                <div class="credits-popup-content">
                    <span class="credits-popup-close">&times;</span>
                    <div class="version">Rolocate: Version 21.3</div>
                    <h2>Credits</h2>
                    <p>This project was created by:</p>
                    <ul>
                        <li>Developer: <a href="https://www.roblox.com/users/545334824/profile" target="_blank">Oqarshi</a></li>
                        <li>Special Thanks: <a href="https://chromewebstore.google.com/detail/btroblox-making-roblox-be/hbkpclpemjeibhioopcebchdmohaieln" target="_blank">Btroblox Team ❤️</a></li>
                        <li>Source Code for Roblox Locate is available at <a href="https://greasyfork.org/en/scripts/522164-roblox-locate" target="_blank">GreasyFork</a></li>
                        <li>Source Code for the invite feature is available at <a href="https://github.com/Oqarshi/Invite" target="_blank">Github</a></li>
                    </ul>
                </div>
            </div>
        `;

		// Add the popup to the document
		const popupContainer = document.createElement('div');
		popupContainer.innerHTML = popupHTML;
		document.body.appendChild(popupContainer);

		// Add event listener to close the popup with animation
		const closeButton = document.querySelector('.credits-popup-close');
		const popup = document.querySelector('.credits-popup');
		closeButton.addEventListener('click', () => {
			popup.style.animation = 'fadeOut 0.5s ease-in-out forwards';
			setTimeout(() => {
				popup.remove(); // Remove the popup from the DOM after animation
			}, 500); // Match the duration of the fadeOut animation
		});
	}

	/*********************************************************************************************************************************************************************************************************************************************
	                                                         End of: This is all the functions for the 8 buttons

	*********************************************************************************************************************************************************************************************************************************************/



	/*********************************************************************************************************************************************************************************************************************************************
	                                                         The Universal Functions

	*********************************************************************************************************************************************************************************************************************************************/


	/*******************************************************
	name of function: disableLoadMoreButton
	description: Disables the "Load More" button
	*******************************************************/
	function disableLoadMoreButton() {
		const loadMoreButton = document.querySelector('.rbx-running-games-load-more');
		if (loadMoreButton) {
			loadMoreButton.disabled = true;
			loadMoreButton.style.opacity = '0.5'; // Optional: Make the button look disabled
			loadMoreButton.style.cursor = 'not-allowed'; // Optional: Change cursor to indicate disabled state
			loadMoreButton.title = 'Disabled by Roblox Locator'; // Set tooltip text
		} else {
			console.warn('Load More button not found!');
		}
	}



	/*******************************************************
	name of function: Loadingbar
	description: Shows or hides a loading bar
	*******************************************************/
	function Loadingbar(disable) {
		const serverListSection = document.querySelector('#rbx-running-games');
		const serverCardsContainer = document.querySelector('#rbx-game-server-item-container');

		if (disable) {
			// Remove server cards and disable the "Load More" button
			serverCardsContainer.innerHTML = '';

			// Create and display the loading bar
			const loadingBar = document.createElement('div');
			loadingBar.id = 'loading-bar';
			loadingBar.style.cssText = `
            width: 100%;
            height: 4px;
            background-color: #1E1E1E;
            position: relative;
            overflow: hidden;
            border-radius: 2px;
            margin-top: 10px;
            `;

			const loadingBarInner = document.createElement('div');
			loadingBarInner.style.cssText = `
            width: 50%;
            height: 100%;
            background-color: #00A2FF;
            position: absolute;
            animation: loading 1.5s infinite ease-in-out;
            border-radius: 2px;
            `;

			// Add animation keyframes
			const styleSheet = document.createElement('style');
			styleSheet.textContent = `
            @keyframes loading {
            0% { left: -50%; }
            100% { left: 100%; }
            }
            `;
			document.head.appendChild(styleSheet);

			loadingBar.appendChild(loadingBarInner);
			serverListSection.appendChild(loadingBar);
		} else {
			// Remove the loading bar
			const loadingBar = document.querySelector('#loading-bar');
			if (loadingBar) {
				loadingBar.remove();
			}
		}
	}



	/*******************************************************
	name of function: fetchPlayerThumbnails
	description: Fetches player thumbnails for up to 5 players. Skips the batch if an error occurs.
	*******************************************************/
	async function fetchPlayerThumbnails(playerTokens) {
		// Limit to the first 5 player tokens
		const limitedTokens = playerTokens.slice(0, 5);

		const body = limitedTokens.map(token => ({
			requestId: `0:${token}:AvatarHeadshot:150x150:png:regular`,
			type: "AvatarHeadShot",
			targetId: 0,
			token,
			format: "png",
			size: "150x150",
		}));

		try {
			const response = await fetch("https://thumbnails.roblox.com/v1/batch", {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
					Accept: "application/json",
				},
				body: JSON.stringify(body),
			});

			// Check if the response is successful
			if (!response.ok) {
				throw new Error(`HTTP error! Status: ${response.status}`);
			}

			const data = await response.json();
			return data.data || []; // Return the data or an empty array if no data is present
		} catch (error) {
			console.error('Error fetching player thumbnails:', error);
			return []; // Return an empty array if an error occurs
		}
	}


	/*******************************************************
	name of function: disableFilterButton
	description: Disables or enables the filter button based on the input.
	*******************************************************/
	function disableFilterButton(disable) {
		const filterButton = document.querySelector('.RL-filter-button');
		const overlayId = 'filter-button-overlay';

		if (filterButton) {
			const parent = filterButton.parentElement;

			if (disable) {
				// Disable the filter button with an overlay
				filterButton.disabled = true;
				filterButton.style.opacity = '0.5'; // Make the button look disabled
				filterButton.style.cursor = 'not-allowed'; // Change cursor to indicate disabled state

				// Create an overlay if it doesn't exist
				let overlay = document.getElementById(overlayId);
				if (!overlay) {
					overlay = document.createElement('div');
					overlay.id = overlayId;
					overlay.style.position = 'absolute';
					overlay.style.top = '0';
					overlay.style.left = '0';
					overlay.style.width = '100%';
					overlay.style.height = '100%';
					overlay.style.backgroundColor = 'transparent'; // Transparent to maintain UI
					overlay.style.zIndex = '9999'; // High z-index to cover the button
					overlay.style.pointerEvents = 'all'; // Block all interactions
					parent.style.position = 'relative'; // Ensure parent is positioned for absolute overlay
					parent.appendChild(overlay);
				}
			} else {
				// Enable the filter button
				filterButton.disabled = false;
				filterButton.style.opacity = '1'; // Restore opacity
				filterButton.style.cursor = 'pointer'; // Restore cursor

				// Remove the overlay if it exists
				const overlay = document.getElementById(overlayId);
				if (overlay) {
					overlay.remove();
				}
			}
		} else {
			console.warn('Filter button not found!');
		}
	}



	async function rbx_card(serverId, playerTokens, maxPlayers, playing, gameId) {
		// Fetch player thumbnails (up to 5)
		const thumbnails = await fetchPlayerThumbnails(playerTokens);

		// Create the server card container
		const cardItem = document.createElement('li');
		cardItem.className = 'rbx-game-server-item col-md-3 col-sm-4 col-xs-6';

		// Create the player thumbnails container
		const playerThumbnailsContainer = document.createElement('div');
		playerThumbnailsContainer.className = 'player-thumbnails-container';

		// Add player thumbnails to the container (up to 5)
		thumbnails.forEach(thumbnail => {
			const playerAvatar = document.createElement('span');
			playerAvatar.className = 'avatar avatar-headshot-md player-avatar';

			const thumbnailImage = document.createElement('span');
			thumbnailImage.className = 'thumbnail-2d-container avatar-card-image';

			const img = document.createElement('img');
			img.src = thumbnail.imageUrl;
			img.alt = '';
			img.title = '';

			thumbnailImage.appendChild(img);
			playerAvatar.appendChild(thumbnailImage);
			playerThumbnailsContainer.appendChild(playerAvatar);
		});

		// Add the 6th placeholder for remaining players
		if (playing > 5) {
			const remainingPlayers = playing - 5;
			const placeholder = document.createElement('span');
			placeholder.className = 'avatar avatar-headshot-md player-avatar hidden-players-placeholder';
			placeholder.textContent = `+${remainingPlayers}`;
			placeholder.style.cssText = `
        background-color: #7b7c7d; /* Gray background */
        color: white;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 50%; /* Fully round */
        font-size: 16px; /* Larger font size */
        width: 60px; /* Larger width */
        height: 60px; /* Larger height */
        `;
			playerThumbnailsContainer.appendChild(placeholder);
		}

		// Create the server details container
		const serverDetails = document.createElement('div');
		serverDetails.className = 'rbx-game-server-details game-server-details';

		// Add server status (e.g., "15 of 15 people max")
		const serverStatus = document.createElement('div');
		serverStatus.className = 'text-info rbx-game-status rbx-game-server-status text-overflow';
		serverStatus.textContent = `${playing} of ${maxPlayers} people max`;
		serverDetails.appendChild(serverStatus);

		// Add the player count gauge
		const gaugeContainer = document.createElement('div');
		gaugeContainer.className = 'server-player-count-gauge border';

		const gaugeInner = document.createElement('div');
		gaugeInner.className = 'gauge-inner-bar border';
		gaugeInner.style.width = `${(playing / maxPlayers) * 100}%`;

		gaugeContainer.appendChild(gaugeInner);
		serverDetails.appendChild(gaugeContainer);

		// Create a container for the buttons
		const buttonContainer = document.createElement('div');
		buttonContainer.className = 'button-container';
		buttonContainer.style.cssText = `
        display: flex;
        gap: 8px; /* Space between buttons */
    `;

		// Add the "Join" button
		const joinButton = document.createElement('button');
		joinButton.type = 'button';
		joinButton.className = 'btn-full-width btn-control-xs rbx-game-server-join game-server-join-btn btn-primary-md btn-min-width';
		joinButton.textContent = 'Join';

		// Add click event to join the server
		joinButton.addEventListener('click', () => {
			Roblox.GameLauncher.joinGameInstance(gameId, serverId);
		});

		buttonContainer.appendChild(joinButton);

		// Add the "Invite" button
		const inviteButton = document.createElement('button');
		inviteButton.type = 'button';
		inviteButton.className = 'btn-full-width btn-control-xs rbx-game-server-invite game-server-invite-btn btn-secondary-md btn-min-width';
		inviteButton.textContent = 'Invite';

		// Add click event to log the invite link
		inviteButton.addEventListener('click', () => {
			const inviteLink = `https://oqarshi.github.io/Invite/?placeid=${gameId}&serverid=${serverId}`;
			//console.log('Copied invite link:', inviteLink);
			navigator.clipboard.writeText(inviteLink).then(() => {
				notifications('Success! Invite link copied to clipboard!', 'success', '🎉');
				//console.log('Invite link copied to clipboard');
			}).catch(err => {
				console.error('Failed to copy invite link:', err);
			});
		});

		buttonContainer.appendChild(inviteButton);

		// Add the button container to the server details
		serverDetails.appendChild(buttonContainer);

		// Assemble the card
		const cardContainer = document.createElement('div');
		cardContainer.className = 'card-item';
		cardContainer.appendChild(playerThumbnailsContainer);
		cardContainer.appendChild(serverDetails);

		cardItem.appendChild(cardContainer);

		// Add the card to the server list
		const serverList = document.querySelector('#rbx-game-server-item-container');
		serverList.appendChild(cardItem);
	}

	/*********************************************************************************************************************************************************************************************************************************************
	                                                         The function for the notification function

	*********************************************************************************************************************************************************************************************************************************************/

	// Create the toast container
	const toastContainer = document.createElement('div');
	toastContainer.id = 'toast-container';
	document.body.appendChild(toastContainer);

	// Define toast styles
	const styles = `
        #toast-container {
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 9999;
            display: flex;
            flex-direction: column;
            align-items: flex-end;
        }

        .toast {
            position: relative;
            min-width: 250px;
            padding: 15px 20px;
            margin-bottom: 10px;
            border-radius: 5px;
            color: white;
            font-family: 'Arial', sans-serif;
            font-size: 14px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            opacity: 0;
            transform: translateX(100%);
            animation: slideIn 0.5s ease-out forwards, bounce 0.5s ease-out, fadeOut 0.5s ease-out 7.5s forwards;
            display: flex;
            align-items: center;
        }

        .toast.success {
            background-color: #4CAF50;
        }

        .toast.error {
            background-color: #F44336;
        }

        .toast.warning {
            background-color: #FF9800;
        }

        .toast.info {
            background-color: #2196F3;
        }

        .toast::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 4px;
            background-color: rgba(255, 255, 255, 0.5);
            animation: progressBar 8s linear forwards;
        }

        .toast .icon {
            margin-right: 10px;
            font-size: 20px;
        }

        @keyframes slideIn {
            to {
                opacity: 1;
                transform: translateX(0);
            }
        }

        @keyframes fadeOut {
            to {
                opacity: 0;
                transform: translateX(100%);
            }
        }

        @keyframes progressBar {
            to {
                width: 0;
            }
        }

        @keyframes bounce {
            0%, 20%, 50%, 80%, 100% {
                transform: translateY(0);
            }
            40% {
                transform: translateY(-20px);
            }
            60% {
                transform: translateY(-10px);
            }
        }
    `;

	// Add styles to the document
	const styleSheet = document.createElement('style');
	styleSheet.type = 'text/css';
	styleSheet.innerText = styles;
	document.head.appendChild(styleSheet);

	// Function to create a toast
	function notifications(message, type, icon) {
		const toast = document.createElement('div');
		toast.className = `toast ${type}`;

		// Add icon
		const iconElement = document.createElement('span');
		iconElement.className = 'icon';
		iconElement.innerHTML = icon;
		toast.appendChild(iconElement);

		// Add message
		const messageElement = document.createElement('span');
		messageElement.innerText = message;
		toast.appendChild(messageElement);

		toastContainer.appendChild(toast);

		// Remove the toast after the animation ends
		setTimeout(() => {
			toast.remove();
		}, 8000);
	}

	/*********************************************************************************************************************************************************************************************************************************************
	                                                         End of function for the notification function

	*********************************************************************************************************************************************************************************************************************************************/

    const serverRegionsByIp = {
        "128.116.0.0": { city: "Hong Kong", country: { name: "Hong Kong", code: "HK" }, latitude: 22.3193, longitude: 114.1694 },
        "128.116.1.0": { city: "Los Angeles", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 34.0522, longitude: -118.2437 },
        "128.116.2.0": { city: "Warsaw", country: { name: "Poland", code: "PL" }, region: { name: "Mazowieckie", code: "14" }, latitude: 52.2297, longitude: 21.0122 },
        "128.116.3.0": { city: "Warsaw", country: { name: "Poland", code: "PL" }, region: { name: "Mazowieckie", code: "14" }, latitude: 52.2297, longitude: 21.0122 },
        "128.116.4.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
        "128.116.5.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.6.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
        "128.116.7.0": { city: "Mumbai", country: { name: "India", code: "IN" }, region: { name: "Mahārāshtra", code: "MH" }, latitude: 19.0760, longitude: 72.8777 },
        "128.116.8.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.9.0": { city: "Mumbai", country: { name: "India", code: "IN" }, region: { name: "Mahārāshtra", code: "MH" }, latitude: 19.0760, longitude: 72.8777 },
        "128.116.10.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.11.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.12.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.13.0": { city: "Amsterdam", country: { name: "Netherlands", code: "NL" }, region: { name: "Noord-Holland", code: "NH" }, latitude: 52.3676, longitude: 4.9041 },
        "128.116.14.0": { city: "Hong Kong", country: { name: "Hong Kong", code: "HK" }, latitude: 22.3193, longitude: 114.1694 },
        "128.116.15.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
        "128.116.16.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
        "128.116.17.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
        "128.116.18.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
        "128.116.19.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
        "128.116.20.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
        "128.116.21.0": { city: "Amsterdam", country: { name: "Netherlands", code: "NL" }, region: { name: "Noord-Holland", code: "NH" }, latitude: 52.3676, longitude: 4.9041 },
        "128.116.22.0": { city: "Atlanta", country: { name: "United States", code: "US" }, region: { name: "Georgia", code: "GA" }, latitude: 33.7490, longitude: -84.3880 },
        "128.116.23.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
        "128.116.24.0": { city: "Atlanta", country: { name: "United States", code: "US" }, region: { name: "Georgia", code: "GA" }, latitude: 33.7490, longitude: -84.3880 },
        "128.116.25.0": { city: "Atlanta", country: { name: "United States", code: "US" }, region: { name: "Georgia", code: "GA" }, latitude: 33.7490, longitude: -84.3880 },
        "128.116.26.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
        "128.116.27.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.28.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.29.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.30.0": { city: "Hong Kong", country: { name: "Hong Kong", code: "HK" }, latitude: 22.3193, longitude: 114.1694 },
        "128.116.31.0": { city: "Warsaw", country: { name: "Poland", code: "PL" }, region: { name: "Mazowieckie", code: "14" }, latitude: 52.2297, longitude: 21.0122 },
        "128.116.32.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
        "128.116.33.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
        "128.116.34.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.35.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
        "128.116.36.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
        "128.116.37.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
        "128.116.38.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
        "128.116.39.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.40.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.41.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.42.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.43.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.44.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.45.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
        "128.116.46.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.47.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.48.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.49.0": { city: "Los Angeles", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 34.0522, longitude: -118.2437 },
        "128.116.50.0": { city: "Singapore", country: { name: "Singapore", code: "SG" }, latitude: 1.3521, longitude: 103.8198 },
        "128.116.51.0": { city: "Sydney", country: { name: "Australia", code: "AU" }, region: { name: "New South Wales", code: "NSW" }, latitude: -33.8688, longitude: 151.2093 },
        "128.116.52.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.53.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.54.0": { city: "Amsterdam", country: { name: "Netherlands", code: "NL" }, region: { name: "Noord-Holland", code: "NH" }, latitude: 52.3676, longitude: 4.9041 },
        "128.116.55.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
        "128.116.56.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.57.0": { city: "San Jose", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3382, longitude: -121.8863 },
        "128.116.58.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
        "128.116.59.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
        "128.116.60.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
        "128.116.61.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.62.0": { city: "Seattle", country: { name: "United States", code: "US" }, region: { name: "Washington", code: "WA" }, latitude: 47.6062, longitude: -122.3321 },
        "128.116.63.0": { city: "Los Angeles", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 34.0522, longitude: -118.2437 },
        "128.116.64.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.65.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
        "128.116.66.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
        "128.116.67.0": { city: "San Jose", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3382, longitude: -121.8863 },
        "128.116.68.0": { city: "Santa Clara", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3541, longitude: -121.9552 },
        "128.116.69.0": { city: "Santa Clara", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3541, longitude: -121.9552 },
        "128.116.70.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.71.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.72.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
        "128.116.73.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
        "128.116.74.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.75.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.76.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.77.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.78.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.79.0": { city: "Singapore", country: { name: "Singapore", code: "SG" }, latitude: 1.3521, longitude: 103.8198 },
        "128.116.80.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.81.0": { city: "Santa Clara", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3541, longitude: -121.9552 },
        "128.116.82.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
        "128.116.83.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
        "128.116.84.0": { city: "Elk Grove Village", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 42.0039, longitude: -87.9706 },
        "128.116.85.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
        "128.116.86.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.87.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.88.0": { city: "Elk Grove Village", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 42.0039, longitude: -87.9706 },
        "128.116.89.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
        "128.116.90.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.91.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.92.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.93.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.94.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.95.0": { city: "Dallas", country: { name: "United States", code: "US" }, region: { name: "Texas", code: "TX" }, latitude: 32.7767, longitude: -96.7970 },
        "128.116.96.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.97.0": { city: "Singapore", country: { name: "Singapore", code: "SG" }, latitude: 1.3521, longitude: 103.8198 },
        "128.116.98.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.99.0": { city: "Atlanta", country: { name: "United States", code: "US" }, region: { name: "Georgia", code: "GA" }, latitude: 33.7490, longitude: -84.3880 },
        "128.116.100.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.101.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.102.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.103.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.104.0": { city: "Mumbai", country: { name: "India", code: "IN" }, region: { name: "Mahārāshtra", code: "MH" }, latitude: 19.0760, longitude: 72.8777 },
        "128.116.105.0": { city: "Santa Clara", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3541, longitude: -121.9552 },
        "128.116.106.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.107.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.108.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.109.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.110.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.111.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.112.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.113.0": { city: "Chicago", country: { name: "United States", code: "US" }, region: { name: "Illinois", code: "IL" }, latitude: 41.8781, longitude: -87.6298 },
        "128.116.114.0": { city: "Ashburn", country: { name: "United States", code: "US" }, region: { name: "Virginia", code: "VA" }, latitude: 39.0438, longitude: -77.4874 },
        "128.116.115.0": { city: "Seattle", country: { name: "United States", code: "US" }, region: { name: "Washington", code: "WA" }, latitude: 47.6062, longitude: -122.3321 },
        "128.116.116.0": { city: "Los Angeles", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 34.0522, longitude: -118.2437 },
        "128.116.117.0": { city: "San Jose", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.3382, longitude: -121.8863 },
        "128.116.118.0": { city: "Hong Kong", country: { name: "Hong Kong", code: "HK" }, latitude: 22.3193, longitude: 114.1694 },
        "128.116.119.0": { city: "Slough", country: { name: "United Kingdom", code: "GB" }, region: { name: "England", code: "ENG" }, latitude: 51.5105, longitude: -0.5950 },
        "128.116.120.0": { city: "Tokyo", country: { name: "Japan", code: "JP" }, region: { name: "Tokyo", code: "13" }, latitude: 35.6895, longitude: 139.6917 },
        "128.116.121.0": { city: "Amsterdam", country: { name: "Netherlands", code: "NL" }, region: { name: "Noord-Holland", code: "NH" }, latitude: 52.3676, longitude: 4.9041 },
        "128.116.122.0": { city: "Paris", country: { name: "France", code: "FR" }, region: { name: "Île-de-France", code: "IDF" }, latitude: 48.8566, longitude: 2.3522 },
        "128.116.123.0": { city: "Frankfurt am Main", country: { name: "Germany", code: "DE" }, region: { name: "Hessen", code: "HE" }, latitude: 50.1109, longitude: 8.6821 },
        "128.116.124.0": { city: "Warsaw", country: { name: "Poland", code: "PL" }, region: { name: "Mazowieckie", code: "14" }, latitude: 52.2297, longitude: 21.0122 },
        "128.116.125.0": { city: "San Mateo", country: { name: "United States", code: "US" }, region: { name: "California", code: "CA" }, latitude: 37.5630, longitude: -122.3255 },
        "128.116.126.0": { city: "Secaucus", country: { name: "United States", code: "US" }, region: { name: "New Jersey", code: "NJ" }, latitude: 40.7895, longitude: -74.0565 },
        "128.116.127.0": { city: "Miami", country: { name: "United States", code: "US" }, region: { name: "Florida", code: "FL" }, latitude: 25.7617, longitude: -80.1918 },
    };

	// Main function does nothing lmao but its ere i guees
	function main() {
		const gameIdMatch = window.location.pathname.match(/\/games\/(\d+)\//);
		if (!gameIdMatch) {
			console.error("Game ID not found in URL!");
			return;
		}

		const gameId = gameIdMatch[1];
	}

	/*******************************************************
	name of function: Initiate the observer
	description: Start observing the document for changes
	*******************************************************/

	// Start observing the document for changes
	observer.observe(document.body, {
		childList: true,
		subtree: true
	});
	main();
})();