Загрузка данных
if not game:IsLoaded() then
game.Loaded:Wait()
end
local Players = game:GetService("Players")
local HttpService = game:GetService("HttpService")
local TeleportService = game:GetService("TeleportService")
local GuiService = game:GetService("GuiService")
local player = Players.LocalPlayer or Players.PlayerAdded:Wait()
task.wait(2.5)
local MAX_PLAYERS = 5
local placeId = game.PlaceId
local currentJobId = game.JobId
local env = (getgenv and getgenv()) or _G
-- Останавливаем любые старые циклы из прошлых запусков скрипта
env.RunId = (env.RunId or 0) + 1
local currentRunId = env.RunId
env.FailedServers = env.FailedServers or {}
env.CandidateServers = env.CandidateServers or {}
env.IsSearching = false
env.IsTeleporting = false
if env.TeleportFailConn then env.TeleportFailConn:Disconnect() end
if env.ErrorMsgConn then env.ErrorMsgConn:Disconnect() end
if env.PlayerAddedConn then env.PlayerAddedConn:Disconnect() end
local cacheFileName = "ServerCache_" .. tostring(placeId) .. ".json"
-- Чтение и запись кэша серверов (чтобы не ловить Rate Limit при перезаходах)
local function loadCache()
if readfile and isfile and isfile(cacheFileName) then
local ok, data = pcall(function()
return HttpService:JSONDecode(readfile(cacheFileName))
end)
if ok and type(data) == "table" and data.time and (os.time() - data.time) < 90 and type(data.servers) == "table" then
return data.servers
end
end
return nil
end
local function saveCache(servers)
if writefile then
pcall(function()
writefile(cacheFileName, HttpService:JSONEncode({
time = os.time(),
servers = servers
}))
end)
end
end
local findAliveServers
local function tryNextServer()
if env.RunId ~= currentRunId then return end
while #env.CandidateServers > 0 do
local targetServerId = table.remove(env.CandidateServers, 1)
if not env.FailedServers[targetServerId] and targetServerId ~= currentJobId then
env.FailedServers[targetServerId] = true
env.IsTeleporting = true
saveCache(env.CandidateServers)
print("[AutoHop] Подключение к серверу: " .. targetServerId .. " (В пуле осталось: " .. #env.CandidateServers .. ")")
pcall(function()
GuiService:ClearError()
end)
local success, err = pcall(function()
TeleportService:TeleportToPlaceInstance(placeId, targetServerId, player)
end)
if not success then
warn("[AutoHop] Ошибка вызова телепорта: " .. tostring(err))
env.IsTeleporting = false
task.wait(1.5)
else
return
end
end
end
env.IsTeleporting = false
env.IsSearching = false
warn("[AutoHop] Пул серверов закончился. Новый запрос к API через 10 сек...")
task.wait(10)
if env.RunId == currentRunId then
findAliveServers(true)
end
end
local function handleFailure(reason)
if env.RunId ~= currentRunId then return end
if not env.IsTeleporting then return end
warn("[AutoHop] Сбой входа (" .. tostring(reason) .. "). Пробуем следующий через 2 сек...")
pcall(function()
GuiService:ClearError()
end)
task.wait(2)
pcall(function()
GuiService:ClearError()
end)
env.IsTeleporting = false
tryNextServer()
end
env.TeleportFailConn = TeleportService.TeleportInitFailed:Connect(function(failedPlayer, teleportResult, errorMessage)
if failedPlayer == player then
handleFailure(tostring(teleportResult.Name) .. " - " .. tostring(errorMessage))
end
end)
env.ErrorMsgConn = GuiService.ErrorMessageChanged:Connect(function(errorMessage)
if errorMessage and errorMessage ~= "" then
handleFailure("Окно ошибки: " .. tostring(errorMessage))
end
end)
-- Запрос к API с автоматическим переключением на резервный прокси при Rate Limit
local function fetchServerPage(cursor)
local endpoints = {
"https://games.roblox.com/v1/games/" .. placeId .. "/servers/Public?sortOrder=Asc&excludeFullGames=true&limit=100",
"https://games.roproxy.com/v1/games/" .. placeId .. "/servers/Public?sortOrder=Asc&excludeFullGames=true&limit=100"
}
for i, baseUrl in ipairs(endpoints) do
local url = baseUrl
if cursor then
url = url .. "&cursor=" .. HttpService:UrlEncode(cursor)
end
local success, decoded = pcall(function()
local response = game:HttpGet(url)
return HttpService:JSONDecode(response)
end)
if success and type(decoded) == "table" and type(decoded.data) == "table" then
return decoded
end
if i == 1 then
warn("[AutoHop] Основной API Roblox временно ограничил запросы, пробуем резервное зеркало...")
task.wait(1)
end
end
return nil
end
findAliveServers = function(ignoreCache)
if env.RunId ~= currentRunId then return end
if env.IsSearching or env.IsTeleporting then return end
env.IsSearching = true
-- 1. Проверяем сохранённый кэш, чтобы не спамить API
if not ignoreCache then
local cached = loadCache()
if cached and #cached > 0 then
print("[AutoHop] Загружено серверов из кэша: " .. #cached)
env.CandidateServers = cached
tryNextServer()
return
end
end
print("[AutoHop] Сканирование серверов (ищем от 1 до " .. (MAX_PLAYERS - 1) .. " игроков)...")
local data = fetchServerPage(nil)
if not data then
warn("[AutoHop] Оба API перегружены (Rate Limit). Ждем 15 сек для снятия блокировки...")
task.wait(15)
env.IsSearching = false
if env.RunId == currentRunId then
findAliveServers(true)
end
return
end
local collected = {}
for _, server in ipairs(data.data) do
if type(server) == "table" and type(server.id) == "string" then
local playing = tonumber(server.playing) or 0
local maxPlayers = tonumber(server.maxPlayers) or 0
local fps = tonumber(server.fps) or 60
local ping = tonumber(server.ping) or 100
-- Ищем серверы, где от 1 до 4 игроков, минимум 2 свободных слота и сервер не завис (FPS >= 30)
if server.id ~= currentJobId and not env.FailedServers[server.id] then
if playing >= 1 and playing < MAX_PLAYERS and (maxPlayers - playing) >= 2 and fps >= 30 and ping <= 500 then
table.insert(collected, { id = server.id, playing = playing })
end
end
end
end
-- Сортируем так, чтобы сначала шли серверы с 1 игроком, затем с 2, 3 и 4
table.sort(collected, function(a, b)
return a.playing < b.playing
end)
env.CandidateServers = {}
for _, item in ipairs(collected) do
table.insert(env.CandidateServers, item.id)
end
if #env.CandidateServers > 0 then
print("[AutoHop] Найдено подходящих серверов: " .. #env.CandidateServers)
saveCache(env.CandidateServers)
tryNextServer()
else
warn("[AutoHop] На первой странице нет серверов меньше " .. MAX_PLAYERS .. " игроков. Повтор через 10 сек...")
task.wait(10)
env.IsSearching = false
if env.RunId == currentRunId then
findAliveServers(true)
end
end
end
local function checkPlayerCount()
if env.RunId ~= currentRunId then return end
local currentCount = #Players:GetPlayers()
if currentCount > MAX_PLAYERS then
if not env.IsSearching and not env.IsTeleporting then
warn("[AutoHop] На сервере " .. currentCount .. " игроков (лимит: " .. MAX_PLAYERS .. "). Ищем новый сервер...")
findAliveServers(false)
end
else
print("[AutoHop] Сервер подходит! Игроков: " .. currentCount .. "/" .. MAX_PLAYERS)
end
end
checkPlayerCount()
env.PlayerAddedConn = Players.PlayerAdded:Connect(function()
task.wait(0.5)
checkPlayerCount()
end)