Загрузка данных
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 = 5
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
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
-- Скрываем всплывающие плашки (Unknown Status / Server Unavailable)
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 parentFrame = v:FindFirstAncestorWhichIsA("Frame")
if parentFrame then parentFrame.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()
return CoreGui.RobloxPromptGui.promptOverlay.ErrorPrompt.Visible
end)
if ok2 and vis then return true end
local ok3, hasToast = pcall(function()
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
return true
end
end
end
return false
end)
return ok3 and hasToast
end
local scanServers
local function hopNext()
if env.RunId ~= myRunId then return end
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 = pcall(function()
TeleportService:TeleportToPlaceInstance(placeId, id, Players.LocalPlayer)
end)
if ok then
-- Сторожевой таймер против "Unknown Status": если за 6 секунд сервер не пустил, берем следующий
task.delay(6, function()
if env.RunId == myRunId and env.Busy and env.AttemptId == thisAttempt then
warn("[AutoHop] Сервер не ответил за 6 сек (Unknown Status / Timeout). Меняем сервер...")
clearErrorPopup()
env.Busy = false
hopNext()
end
end)
return
end
task.wait(1.5)
end
end
env.Busy = 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
-- Мониторинг ошибок (277, 772, Unknown Status, Unavailable) прямо с экрана загрузки
task.spawn(function()
while env.RunId == myRunId do
if hasError() then
warn("[AutoHop] Перехвачена ошибка входа! Переключаем сервер...")
clearErrorPopup()
task.wait(1.5)
if env.RunId ~= myRunId then break end
env.AttemptId = env.AttemptId + 1
env.Busy = false
hopNext()
task.wait(4)
end
task.wait(0.8)
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(1.5)
env.Busy = 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
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 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"
}
local data = nil
for _, url in ipairs(urls) do
local ok, res = pcall(function() return HttpService:JSONDecode(game:HttpGet(url)) end)
if ok and type(res) == "table" and type(res.data) == "table" then
data = res
break
end
task.wait(1)
end
if not data then
warn("[AutoHop] Лимит API, ждем 15 сек...")
task.wait(15)
env.Busy = false
if env.RunId == myRunId then scanServers(true) end
return
end
local list = {}
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 >= 2 and pl < MAX_PLAYERS and (max - pl) >= 5 and fps >= 45 and ping >= 20 and ping <= 350 then
table.insert(list, { id = s.id, pl = pl })
end
end
end
end
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
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 and #Players:GetPlayers() > MAX_PLAYERS then
warn("[AutoHop] На сервере больше " .. MAX_PLAYERS .. " игроков! Меняем сервер...")
scanServers(false)
end
end
checkCount()
env.JoinConn = Players.PlayerAdded:Connect(function()
task.wait(0.5)
checkCount()
end)