Загрузка данных
local Players = game:GetService("Players")
local HttpService = game:GetService("HttpService")
local TeleportService = game:GetService("TeleportService")
local GuiService = game:GetService("GuiService")
local CoreGui = game:GetService("CoreGui")
local MAX_PLAYERS = 3
local placeId = game.PlaceId
local currentJobId = game.JobId
local env = (getgenv and getgenv()) or _G
env.RunId = (env.RunId or 0) + 1
local myRunId = env.RunId
env.AttemptId = 0
env.Failed = env.Failed or {}
env.Pool = env.Pool or {}
env.Busy = false
env.IsHopping = false
if env.FailConn then env.FailConn:Disconnect() end
if env.JoinConn then env.JoinConn:Disconnect() end
local cacheFile = "HopCache_" .. tostring(placeId) .. ".json"
local function loadCache()
if isfile and readfile and isfile(cacheFile) then
local ok, res = pcall(function() return HttpService:JSONDecode(readfile(cacheFile)) end)
if ok and type(res) == "table" then
for k, v in pairs(res.failed or {}) do env.Failed[k] = v end
if res.time and (os.time() - res.time) < 120 and type(res.pool) == "table" then
return res.pool
end
end
end
return nil
end
local function saveCache()
if writefile then
pcall(function()
writefile(cacheFile, HttpService:JSONEncode({ time = os.time(), pool = env.Pool, failed = env.Failed }))
end)
end
end
local function clearErrorPopup()
pcall(function()
GuiService:ClearError()
local overlay = CoreGui:FindFirstChild("RobloxPromptGui") and CoreGui.RobloxPromptGui:FindFirstChild("promptOverlay")
if overlay then
local prompt = overlay:FindFirstChild("ErrorPrompt")
if prompt and prompt.Visible then
local btn = prompt:FindFirstChildWhichIsA("TextButton", true) or prompt:FindFirstChildWhichIsA("ImageButton", true)
if btn and firesignal then firesignal(btn.MouseButton1Click) end
prompt.Visible = false
end
end
for _, v in ipairs(CoreGui:GetDescendants()) do
if v:IsA("TextLabel") and v.Visible then
local txt = string.lower(v.Text or "")
if string.find(txt, "unknown status") or string.find(txt, "unavailable") or string.find(txt, "ended") then
local frame = v:FindFirstAncestorWhichIsA("Frame")
if frame then frame.Visible = false end
end
end
end
end)
end
local function hasError()
local ok1, code = pcall(function() return GuiService:GetErrorCode() end)
if ok1 and code and code ~= Enum.ConnectionError.OK then return true end
local ok2, vis = pcall(function()
local promptGui = CoreGui:FindFirstChild("RobloxPromptGui")
local overlay = promptGui and promptGui:FindFirstChild("promptOverlay")
local prompt = overlay and overlay:FindFirstChild("ErrorPrompt")
return (promptGui and promptGui.Enabled ~= false) and (prompt and prompt.Visible == true) or false
end)
return ok2 and vis
end
local scanServers
local function hopNext()
if env.RunId ~= myRunId then return end
if env.IsHopping then return end
env.IsHopping = true
if #env.Pool == 0 then env.Pool = loadCache() or {} end
while #env.Pool > 0 do
local id = table.remove(env.Pool, 1)
if not env.Failed[id] and id ~= currentJobId then
env.Failed[id] = true
env.Busy = true
env.AttemptId = env.AttemptId + 1
local thisAttempt = env.AttemptId
saveCache()
clearErrorPopup()
print("[AutoHop] Подключение: " .. id .. " (В пуле осталось: " .. #env.Pool .. ")")
local ok, err = pcall(function()
TeleportService:TeleportToPlaceInstance(placeId, id, Players.LocalPlayer)
end)
if ok then
-- Сторожевой таймер: ждем 7 секунд до следующего шага
task.delay(7, function()
if env.RunId == myRunId and env.AttemptId == thisAttempt and game.JobId == currentJobId then
warn("[AutoHop] Сервер не ответил (Timeout). Берем следующий...")
clearErrorPopup()
env.Busy = false
env.IsHopping = false
hopNext()
end
end)
env.IsHopping = false
return
else
warn("[AutoHop] Ошибка вызова (кулдаун движка): " .. tostring(err) .. ". Ждем 3.5 сек...")
task.wait(3.5)
end
end
end
env.Busy = false
env.IsHopping = false
if hasError() then
clearErrorPopup()
pcall(function() TeleportService:Teleport(placeId) end)
return
end
task.wait(8)
if env.RunId == myRunId then scanServers(true) end
end
-- Обработка системных ошибок Roblox
task.spawn(function()
while env.RunId == myRunId do
if hasError() then
warn("[AutoHop] Окно ошибки Roblox! Смена сервера через 2 сек...")
clearErrorPopup()
task.wait(2)
if env.RunId ~= myRunId then break end
env.AttemptId = env.AttemptId + 1
env.Busy = false
env.IsHopping = false
hopNext()
task.wait(5)
end
task.wait(1)
end
end)
env.FailConn = TeleportService.TeleportInitFailed:Connect(function(p)
if env.RunId == myRunId and (not Players.LocalPlayer or p == Players.LocalPlayer) then
env.AttemptId = env.AttemptId + 1
clearErrorPopup()
task.wait(2)
env.Busy = false
env.IsHopping = false
hopNext()
end
end)
if not game:IsLoaded() then game.Loaded:Wait() end
local lp = Players.LocalPlayer or Players.PlayerAdded:Wait()
task.wait(2.5)
currentJobId = game.JobId
local function fetchPage(cursor)
local urls = {
"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 _, base in ipairs(urls) do
local url = cursor and (base .. "&cursor=" .. HttpService:UrlEncode(cursor)) or base
local ok, res = pcall(function() return HttpService:JSONDecode(game:HttpGet(url)) end)
if ok and type(res) == "table" and type(res.data) == "table" then
return res
end
task.wait(1)
end
return nil
end
scanServers = function(ignoreCache)
if env.RunId ~= myRunId or env.Busy then return end
env.Busy = true
if not ignoreCache then
local cached = loadCache()
if cached and #cached > 0 then
env.Pool = cached
hopNext()
return
end
end
local list = {}
local cursor = nil
local pages = 0
repeat
local data = fetchPage(cursor)
if not data then
warn("[AutoHop] Лимит API, ждем 12 сек...")
task.wait(12)
break
end
for _, s in ipairs(data.data) do
if type(s) == "table" and type(s.id) == "string" then
local pl = tonumber(s.playing) or 0
local max = tonumber(s.maxPlayers) or 0
local fps = tonumber(s.fps) or 60
local ping = tonumber(s.ping) or 100
if s.id ~= currentJobId and not env.Failed[s.id] then
if pl >= 1 and pl < MAX_PLAYERS and (max - pl) >= 2 and fps >= 30 and ping <= 450 then
table.insert(list, { id = s.id, pl = pl })
end
end
end
end
pages = pages + 1
cursor = data.nextPageCursor
if #list >= 15 or not cursor or pages >= 3 then break end
task.wait(0.3)
until false
table.sort(list, function(a, b) return a.pl > b.pl end)
env.Pool = {}
for _, item in ipairs(list) do table.insert(env.Pool, item.id) end
if #env.Pool > 0 then
print("[AutoHop] Собрано серверов: " .. #env.Pool)
saveCache()
hopNext()
else
warn("[AutoHop] Подходящих серверов нет, повтор через 10 сек...")
task.wait(10)
env.Busy = false
if env.RunId == myRunId then scanServers(true) end
end
end
local function checkCount()
if env.RunId == myRunId and not env.Busy then
local count = #Players:GetPlayers()
if count > MAX_PLAYERS then
warn("[AutoHop] На сервере " .. count .. " игроков (лимит: " .. MAX_PLAYERS .. ")! Меняем сервер...")
scanServers(false)
else
print("[AutoHop] Сервер подходит! Игроков: " .. count .. "/" .. MAX_PLAYERS)
end
end
end
checkCount()
env.JoinConn = Players.PlayerAdded:Connect(function()
task.wait(0.5)
checkCount()
end)