repeat task.wait() until game:IsLoaded() -- ★★ HẠ-RENDER (đòn bẩy CPU client LỚN NHẤT — bot headless ko cần vẽ; game SAB render đầy brainrot chuyển động + VFX). -- MẶC ĐỊNH TẮT (false) vì chưa verify live (MCP rớt lúc thêm). StreamingEnabled stream theo VỊ TRÍ character (KHÔNG theo -- render) ⇒ về lý thuyết tắt render KO ảnh hưởng replicate plot/pet. ĐỔI = true để bật, rồi THEO DÕI: scan còn đủ pet/plot ko? -- Nếu miss → tắt lại (hoặc chỉ giữ phần QualityLevel/shadows = an toàn tuyệt đối, bỏ Set3dRenderingEnabled = phần mạnh nhất nhưng rủi ro nhất). local LOW_RENDER = true -- ★ BẬT render-off (như đối thủ): tắt 3D = CPU vẽ ~0 → máy "thoát xác" chạy nhẹ. ⚠️ TEST crash vài phút; nếu crash → comment dòng Set3dRenderingEnabled bên dưới (giữ phần quality-low an toàn) hoặc đổi lại false. if LOW_RENDER then -- (a) AN TOÀN TUYỆT ĐỐI (chỉ giảm chi tiết hình, ko đụng DataModel): quality thấp nhất + tắt bóng/sương. pcall(function() settings().Rendering.QualityLevel = Enum.QualityLevel.Level01 end) pcall(function() game:GetService("UserSettings"):GetService("UserGameSettings").SavedQualityLevel = Enum.SavedQualitySetting.QualityLevel1 end) pcall(function() local L = game:GetService("Lighting") L.GlobalShadows = false L.FogEnd = 9e9 L.Brightness = 0 L.ExposureCompensation = -math.huge end) -- (b) MẠNH NHẤT (tắt hẳn 3D render = gần như 0 CPU vẽ) — RỦI RO NHẤT, tách riêng để dễ bỏ nếu nghi ảnh hưởng: -- pcall(function() game:GetService("RunService"):Set3dRenderingEnabled(false) end) end -- ★ cloneref TẤT CẢ service (né detect so-sánh-reference). LocalPlayer = Players.LocalPlayer (KHÔNG -- cloneref chính LocalPlayer → giữ instance thật; mọi so sánh self trong script dùng .UserId nên an toàn). local cloneref = (typeof(cloneref) == "function" and cloneref) or (typeof(clonereference) == "function" and clonereference) or function(o) return o end local Players = cloneref(game:GetService("Players")) local Workspace = cloneref(game:GetService("Workspace")) local ReplicatedStorage = cloneref(game:GetService("ReplicatedStorage")) local HttpService = cloneref(game:GetService("HttpService")) local TeleportService = cloneref(game:GetService("TeleportService")) local LocalPlayer = Players.LocalPlayer -- ── ANTI-TAMPER (lõi): chụp ref builtin NGAY lúc load. Nếu sau đó global bị THAY (hook runtime -- để moi key) → coreTampered()=true. FP thấp: chỉ trip khi global đổi SAU load (không trip với -- executor pre-hook lúc load, vì lúc đó ta đã chụp đúng bản pre-hook). Wiring + mode ở dưới CONFIG. local _rawCore = { c = string.char, b = string.byte, t = table.concat, f = math.floor, s = string.sub } local function coreTampered() return not ( rawequal(string.char, _rawCore.c) and rawequal(string.byte, _rawCore.b) and rawequal(table.concat, _rawCore.t) and rawequal(math.floor, _rawCore.f) and rawequal(string.sub, _rawCore.s) ) end local AntiTamperCheck -- forward decl: trả true → crypto POISON key (gán sau CONFIG, đọc CONFIG.AntiTamper) -- ============================================================================ -- WNLITE SIÊU-LITE — chỉ ENCRYPT jobId ("wnotifier.com-"): PAID (cdev) + FREE (free-log Discord). XOR THUẦN: c = bxor(byte, K[pos]). -- Bỏ pos_term + bỏ decrypt/validUUID (scanner ko giải). SECRET giấu (deob SENC). SENC_PAID GIỐNG HỆT ClientSab. -- ★ ĐỔI KEY = đổi 2 mảng SENC (GIỐNG HỆT ClientSab). ⚠️ Đổi cipher (bỏ pos_term) → deploy CÙNG ClientSab. -- ============================================================================ local WNLite = (function() local sbyte, bxor = string.byte, bit32.bxor local SENC_PAID = { 0x7a, 0xc4, 0x1f, 0x93, 0x58, 0xe2, 0x0b, 0xbd, 0x46, 0xf1, 0x2d, 0x89, 0xd6, 0x30, 0xa7, 0x6e, 0x14, 0xcb, 0x82, 0x3f, 0xe9, 0x55, 0x9c, 0x08, 0xb3, 0x67, 0xda, 0x41, 0xfe, 0x22, 0x8d, 0x70, 0xa1, 0x5b, 0xc6, 0x19, 0x94, 0x2f, 0xe0, 0x4c, 0xb8, 0x03, 0x76, 0xdf, 0x38, 0x9a, 0x61, 0xed, } local SENC_FREE = { 0x2e, 0x91, 0x5c, 0xf8, 0x03, 0xa6, 0x4b, 0xdd, 0x70, 0x17, 0xba, 0x66, 0xc1, 0x38, 0x8f, 0x24, 0xe5, 0x59, 0xb2, 0x0d, 0x97, 0x42, 0xfb, 0x2a, 0x86, 0xd3, 0x60, 0x0e, 0xa9, 0x74, 0xcf, 0x33, 0x9e, 0x48, 0xe1, 0x7b, 0x15, 0xbc, 0x57, 0xf0, 0x29, 0x84, 0xdb, 0x36, 0x6d, 0xc8, 0x11, 0xa2, } local function deob(senc) local s = {} for i = 1, #senc do s[i] = bxor(senc[i], ((i * 37 + 0xA3) % 256)) end return s, #senc end local KP, NP = deob(SENC_PAID) local KF, NF = deob(SENC_FREE) local function mkM(K) local m = {} for i = 1, #K do m[i] = (K[i] * 29 + 0x5B + i * 13) % 256 end return m end local MP, MF = mkM(KP), mkM(KF) local function encrypt(jobId, free) jobId = tostring(jobId) local K, n, M = (free and KF or KP), (free and NF or NP), (free and MF or MP) local poison = (AntiTamperCheck and AntiTamperCheck()) and true or false local b = {} for i = 1, #jobId do local kk = K[((i - 1) % n) + 1] if poison then kk = bxor((kk + 0x6d + i * 11) % 256, 0x5a) end b[i] = bxor((sbyte(jobId, i) + kk) % 256, M[((i - 1) % n) + 1]) end for i = 2, #b do b[i] = bxor(b[i], b[i - 1]) end local hex = {} for i = 1, #b do hex[i] = ("%02x"):format(b[i]) end return "wnotifier.com-" .. table.concat(hex) end return { encrypt = encrypt } end)() local _encJobCache -- ★★ RAW_JOBID: true = gửi jobId THÔ (bỏ encrypt WNLite) → nhanh + consumer ĐỌC RAW (ko decode). false = encrypt như cũ. -- ⚠️ Đặt ở ĐẦY (ko trong CONFIG) vì encJob định nghĩa TRƯỚC CONFIG. ⚠️ CONSUMER (ClientSab/local relay) PHẢI khớp: đọc raw jobId. local RAW_JOBID = true -- ★ false = MÃ HOÁ jobId WNLite ("wnotifier.com-") trước khi gửi cdev → ClientSab giải mã. true = gửi thô (consumer đọc raw). -- mã hoá jobId BẤT KỲ (free=true → key FREE). Lỗi → "wnotifier.com-?" (KHÔNG lộ raw). local function encJobOf(j, free) local ok, v = pcall(function() return WNLite.encrypt(tostring(j), free) end) return (ok and v) or "wnotifier.com-?" end -- mã hoá jobId server hiện tại (cache). encJob() = key PAID (realtime cdev). (encJobFree ĐÃ XOÁ cùng free-log.) -- ★ RAW_JOBID=true → trả game.JobId THÔ (ko encrypt, ko cache — tostring rẻ; jobId cố định/server). local function encJob() if RAW_JOBID then return tostring(game.JobId) end if not _encJobCache then _encJobCache = encJobOf(game.JobId, false) end return _encJobCache end -- ============================================================================ -- CONFIG ── chỉnh ở đây ── -- ============================================================================ local CONFIG = { JoinPublic = true, -- ★★ BASE DETECTOR — true = bật quét pet (AnimalList) + gửi CDEV WS/webhook + leave OG/PEAK; false = chỉ hop. BaseDetector = true, -- ★★ CÔNG TẮC TỔNG WEBHOOK DISCORD — false = TẮT SẠCH mọi webhook (tier + monitor + free-log). true = bật lại. -- (KHÔNG ảnh hưởng WS POST cdev realtime — đó là core, ko phải webhook.Brainrot Notify GameData/Hub cũng riêng.) WebhooksEnabled = true, -- ★★ DYNAMIC FPS BOOST (tiết kiệm CPU fleet + phản ứng nhanh lúc có pet — như đối thủ "boost lúc thấy pet rồi về"). -- Baseline thấp = nhẹ khi server vắng; có HOẠT ĐỘNG pet (plot mới claim / AnimalList đổi) → bùng fps cao N giây rồi tự về. -- ⚠️ FpsBase PHẢI khớp setfpscap(...) ở ĐẦU FILE (dòng 2) — đổi baseline thì đổi CẢ 2. FpsBoost ≤ FpsBase = TẮT boost. FpsBase = 500, -- baseline (server vắng) — khớp setfpscap dòng 2 FpsBoost = 500, -- bùng khi có pet → event/POST/hop xử lý nhanh (60 = ~16ms/frame thay vì 100ms ở fps 10) FpsBoostSec = 2, -- giữ boost N giây sau hoạt động gần nhất (gia hạn nếu pet tiếp tục đổ về) BoostOnPlayerJoin = false, -- ★ boost fps NGAY lúc player MỚI join; AnimalList OnChanged cũng tự boost khi channel event khả dụng. BoostOnJoinSec = 3, -- ★ giữ boost N giây sau mỗi player join (đủ load → claim plot → đặt pet). "đến khi scan xong plot" xấp xỉ bằng cửa sổ này; pet đổ về sẽ tự gia hạn qua OnChanged -- ★★★ RAW FAST MODE — strip xử lý cho POST THÔ + NHANH NHẤT (cạnh tranh tốc độ). Bật lại = đổi từng cờ. -- ⚠️ Đánh đổi: nhiều POST hơn (no dedup/no tier) + CONSUMER phải đọc jobId RAW (RawJobId=true). cdev WS vẫn là core. -- (Dedup ĐÃ XOÁ — globalSeen bỏ; mọi find đều POST) TierFilter = true, -- false = TẮT lọc tier → POST MỌI con (ko cần nằm TIER_NAMES, ko min $/s). Con ko-tier → POST nhưng KHÔNG hop/kick (chỉ OG/PEAK/HIGH mới hop). -- (RawJobId = gửi jobId THÔ: cờ `RAW_JOBID` ở ĐẦU FILE — vì encJob định nghĩa TRƯỚC CONFIG nên ko đọc được CONFIG.) -- ── Webhook theo TIER ($/s). OG route theo RARITY (con rarity OG). ── WebhookLow = "https://discord.com/api/webhooks/1501055459984015462/Jn7P668MmPsXwpC8tBq9cLWsavcVXzp_ccP6LnHlcwgaHrzPuvRELIHKw03hPpUbVkU9", -- LOW WebhookMid = "https://discord.com/api/webhooks/1499101196328374432/bOoBzfWUf1Zzj_9EMx2BnCINZIWbvMNMYlIskiaUwFVvRUU1M7jQqDSweKrY6WU1YWvz", -- MID WebhookHigh = "https://discord.com/api/webhooks/1501379473415733410/gqLcwZ1Ab6U6sk_OoG5i-L1lL5jnckYodYDhLXjfvQt8U6OR7boC-xh5OQrTEKnVpA7T", -- ★★ HIGH: DÁN WEBHOOK URL VÀO ĐÂY (đang trống → con HIGH sẽ KHÔNG gửi) WebhookPeak = "https://discord.com/api/webhooks/1432276698715525130/x_-TcSRZcz8xmby0fqYq4cYV6F_tVSsRoTTm41kYn8r9JcBeMmG9yoyKUoQteocjvqDg", -- PEAK WebhookOG = "https://discord.com/api/webhooks/1380320735322439824/ZS1B67uZaqKuFVVTBlPvVgQllTA_O3hCeH9JTuyF2B-83b5dXw575LwFI3Jw0bJenGW3", -- OG PatchFailureWebhook = "https://discord.com/api/webhooks/1501249978302205952/QO7AbeFJJCy0g0HtvkFraDXN2ClyI7gmsiZB10edpXw9Bja4mM_6NVdOjWzmM9me-zsp", -- tùy chọn: alert một lần khi patch quá 3 phút vẫn không thành công -- (MonitorWebhook ĐÃ XOÁ — sendMonitorWebhook dead, call site đã comment) -- (GameData ĐÃ XOÁ — postGameData + tierToCategory bỏ; token d491ec 401, kênh /logs ko dùng) -- ★ MIN GỬI THEO TIER ($/s): low=200M, mid=50M, high/peak/og = 0 (gửi luôn). Con phải nằm trong TIER_NAMES. TierMinSend = { LOW = 200000000, MID = 50000000, HIGH = 0, PEAK = 0, OG = 0 }, OGMoney = 1000000000, -- ★ OG KHÔNG tính $/s (fast-path snipe) → gửi ĐẠI giá trị này (bỏ genOf/genFromData). PHẢI ≥ money-filter OG bên consumer để ko bị lọc. -- ★ NÂNG TIER theo $/s: con ≥ HighFloorGen mà đang free(ngoài TIER_NAMES)/LOW/MID → ÉP lên HIGH (hàng to luôn báo high + hop). 0 = tắt. HighFloorGen = 1000000000, -- 1B/s -- (CONFIG.FreeLog ĐÃ XOÁ — bỏ post free theo yêu cầu) -- ★ MACHINE DETECT — DATA-DRIVEN (method scanner remake): con đang ở MÁY active = KHÔNG cướp được, đọc qua -- item.Machine.Active từ Synchronizer (bỏ heuristic overhead/Transparency cũ — nhẹ CPU). machineActive() = boolean. FuseDetect = { Skip = true, -- compatibility-only: patched scanner luôn bỏ con đang ở máy active. }, -- ★ Chống gửi trùng: MỖI OWNER chỉ gửi 1 LẦN / mỗi lần exec (cache local, reset khi inject lại). Xem sentOwners. WebhookSpacing = 0, -- POST-speed: ko giãn cách (429 đã có backoff riêng trong sendWebhook) WebhookMaxWaitSec = 20, -- ★ retry webhook theo DEADLINE: kiên nhẫn xuyên cửa sổ 429 tối đa N giây (mọi kênh đối xử như nhau → ko còn "monitor có, tier ko") -- (SkipMachineWebhook ĐÃ XOÁ — con đang máy bị bỏ ngay ở makePet/petFromSyncItem nên ko tới webhook) SendQueueMax = 80, -- ★ cap hàng đợi gửi (429 chặn lâu → queue phình); đầy → bỏ con CŨ nhất WebhookCacheTTL = 1200, -- ★ DEDUP BỀN qua hop: cùng 1 con (owner|tên|mut|$/s) đã gửi thì N giây sau mới gửi lại (chống spam khi hop re-inject) DetectConveyor = "off", -- "always" | "event" (patched Events/Bee) | "off". ConveyorDelay = 0.2, -- compatibility-only; logical intake dispatches after validated trait lookup. -- ★ DEAD CODE REMOVED: BaseScanDelay (workspace scanning only, not used with Synchronizer) -- ★ DEAD CODE REMOVED: SettleQuiet (workspace scanning only, not used with Synchronizer) UseSynchronizer = true, -- true = use the fail-closed patched Synchronizer:Get base detector. SyncPollSec = 2, -- legacy compatibility field; patched detector has no permanent poll. SyncModuleWaitSec = 8, -- bounded readiness and patched-Get batch budget. JoinSettle = 0.1, -- settle after a live Plots root before patched base intake starts. FastPath = false, -- (DEAD/legacy) — thay bằng InstantOG + SettleQuiet bên dưới. Giữ field để ko vỡ config cũ. -- ★★ TỐI ƯU POST (mọi tier, KHÔNG miss best) — xem hookPlot: InstantOG = true, -- ★ OG = tier ĐỈNH tuyệt đối → gửi NGAY khi thấy (KHÔNG chờ debounce). AN TOÀN 100%: ko gì vượt OG; 2 OG khác $ = CÙNG jobId/server → auto-join y hệt. (Chỉ OG; PEAK/HIGH instant sẽ unsafe vì OG có thể stream cùng burst.) SettleQuiet = 0.05, -- ★ SIẾT (user): 0.15→0.05 = quét ~1 frame sau add cuối thay vì 2. (Chỉ path workspace; Synchronizer ko dùng.) Rủi ro: burst nhiều-frame có thể bị cắt → instant-OG vẫn lo OG. SettleMax = 0.8, -- ★ trần chống chờ vô tận khi add dồn dập liên tục (burst quá dài → vẫn quét tại đây). DiscordInvite = "https://discord.gg/wblox", -- ★ link discord gắn dưới mỗi webhook (để trống "" nếu không muốn) OGRoleTag = "", -- ★ tag role này khi nổ OG (để trống "" nếu không muốn) PlaceId = 109983668079237, -- SAB main NewPlayersPlaceId = 96342491571673, -- ★ bản "[New Players]" (nhà giá trị thấp) → vào là hop sang Main ngay ScriptUrl = "", -- ★ (tùy chọn) URL raw của script này → tự chạy lại sau mỗi hop (queue_on_teleport). -- Để TRỐNG nếu anh đã có autoexec re-inject lúc join. -- ★ PRESENCE (hobeojob /api/presence) — POST heartbeat {account(username),placeId,jobId,players,ping,hops,uptime}. Presence = { Enabled = false, -- bỏ post presence theo yêu cầu Interval = 30, -- post 1 lần NGAY khi vào sv, rồi mỗi N giây (180 = 3') }, -- ★ FRIEND REGISTRY (hobeojob /api/usernames) — CHỈ GET (KHÔNG post username). Lấy list username fleet 1 lần lúc join -- → nếu có clone fleet chung server thì hop (hasFriendClone). (Username do hệ khác/presence-backend populate.) FriendRegistry = { Enabled = true, -- ★ RETRY GET /api/usernames (list fleet ĐÔNG → host chậm/timeout → 1 GET fail = registry RỖNG = ko detect clone = kẹt 2-3 acc/sv). -- NGUON FLEET = /api/presence (acc DANG CHAY SCRIPT), KHONG dung /api/usernames/{place} MaxBytes = 12 * 1024 * 1024, -- tran payload: to hon thi BO (tranh treo client). presence ~4,3 MB. -- CHAY LAI DUNG MOT LAN sau N giay (bat clone vao SAU + vot lai GET hong luc exec). -- Chay xong la THOI, khong lap tiep - khac han RefreshInterval/Standoff (dang tat). RecheckAfterSec = 300, -- 0 = tat, khong kiem tra lan hai GetTries = 4, -- số lần thử GET mỗi lượt refresh (1 = ko retry) GetRetryGap = 3, -- giây nghỉ giữa các lần GET fail (host đang quá tải → đợi rồi thử lại) -- ★ CACHE ĐĨA (writefile/readfile) — CHIA SẺ giữa MỌI tab trên máy: GET 1 lần ghi file, còn-hạn thì ĐỌC FILE (KHÔNG GET). -- → cả fleet chỉ GET ~1 lần/CacheTTL (giảm tải host), tab vào sau dùng luôn cache tab trước. GET fail hết retry → fallback cache CŨ. CacheTTL = 300, -- 30' cache còn "tươi" → trong khoảng này chỉ đọc file, KHÔNG gọi mạng (0 = tắt cache, GET như cũ) -- ★ REFRESH ĐỊNH KỲ: GET lại list + check clone mỗi N giây → recover GET-fail lúc join + bắt clone vào SAU. RefreshInterval = 800, -- 5' refresh registry 1 lần (0 = chỉ GET lúc join, ko refresh định kỳ) -- ★ DEADLOCK-BREAKER: clone fleet ở CHUNG sv LIÊN TỤC đủ grace (lẽ ra nó tự hop mà ko hop → GET-fail/kẹt) → acc này hop phá kẹt. -- Tie-break theo RANK UserId (acc UserId NHỎ hop TRƯỚC): grace_thực = GraceSeconds + rank*StaggerSeconds. -- → đúng 1 acc rời mỗi đợt (acc kia thấy clone biến mất sẽ RESET, ở lại) → KHÔNG bao giờ cả 2 cùng hop / re-collide vô hạn. -- ⚠ StandoffEnabled = false: TAT han deadlock-breaker dinh ky (khoi task.spawn -- duoi JOIN). Mo hinh gio la "CHI CHECK LUC EXEC": script vua chay -> lay list -- fleet -> thay clone trong sv thi HOP NGAY, khong thay thi O LAI. Ai chay -- truoc thi nguoi do o, vi quyet dinh chi dua tren trang thai server luc khoi -- chay — KHONG dung UserId/rank/grace/stagger nua. StandoffEnabled = true, GraceSeconds = 180, -- (chi con y nghia khi StandoffEnabled = true) StaggerSeconds = 120, -- mỗi bậc rank cộng N giây grace. PHẢI > thời gian hop TỐI ĐA khi CÓ server (~MaxRetries×(WaitPerTry+RetryBackoff) ≈ 90-100s) -- → acc rank-trước hop xong (rời sv) TRƯỚC khi acc rank-sau tới hạn ⇒ đúng 1 acc hop/đợt, ko double-hop. -- (Lúc NO-SERVER cả 2 acc đều kẹt ko hop được → ko double-hop HẠI; nên KHÔNG cần > KickAfterFailSeconds(700).) }, IncludeOwnBase = false, -- false = BỎ QUA nhà của chính bot (chỉ scan nhà NGƯỜI KHÁC để steal) ShowImage = false, -- ★ ẢNH TẮT (tối ưu tốc độ POST) — không fetch ảnh dưới mọi hình thức ImageSize = 500, -- size ảnh wiki (pithumbsize, px) ImageMeshFallback = false, -- wiki ko có page → false = ko ảnh; true = render mesh 3D (xám) thay thế Hop = { Enabled = true, -- false = chỉ ở lại scan, không hop -- ── ĐIỀU KIỆN HOP (solo kiểm tra theo timer; các điều kiện khác dùng monitor hiện có) ── HopOnHit = true, -- compatibility-only: OG/PEAK chỉ gọi _killLeave sau khi CDEV WebSocket gửi thành công. HopDelayAfterWS = 1, -- compatibility-only. KickAfterSendSec = 0.2, -- compatibility-only. ScanBeforeKick = true, -- compatibility-only. ScanSettleSec = 3, -- compatibility-only. ScanMaxStaySec = 8, -- compatibility-only. DeliverMaxWait = 0, -- compatibility-only. -- (FalseSuccessSec / NoJoinHopMinutes / KickAfterFailSeconds ĐÃ XOÁ theo yêu cầu — bỏ lưới chống-kẹt: hop/teleport fail thì cứ thử lại, KHÔNG tự kick) FullPlayers = 7, -- ★ server ≥ N người (định nghĩa "đầy") -- ★★ ĐIỀU KIỆN HOP còn lại: [1] chỉ còn bot + [2] friend-clone + [3] 8 người-5'. (HopSeven / HopRefresh2h / HopAllOthersPoor ĐÃ XOÁ theo yêu cầu.) HopWhenSolo = true, -- [1] true = hop khi server chỉ còn bot SoloCheckInterval = 60, -- [1] kiểm tra server size mỗi 5 phút HopFull8 = true, -- [3] server ĐẦY 8 người liên tục FullSeconds → hop FullSeconds = 60, -- [3] 8 người (kín slot) liên tục N giây (5') → hop: ko nạn nhân mới vào được nữa -- ★★ (true/false — mặc định FALSE, bật khi cần) HOP NGAY khi server > PoorMinPlayers người mà MỌI người khác đọc được tiền đều < PoorCashThreshold (nghèo, ko đáng steal). -- ⚠️ Cần ĐỌC ĐƯỢC tiền player (playerCash thử leaderstats → attribute → Synchronizer channel). Đọc ko ra player nào = coi KHÔNG nghèo (KHÔNG hop nhầm). -- Chỉ hop khi đọc được ≥ nửa số người khác VÀ tất cả đều nghèo. BẬT rồi TEST (Debug=true xem log) trước khi tin. HopPoorServer = true, PoorMinPlayers = 1, -- > N người mới xét (trên 6 = từ 7 trở lên) PoorCashThreshold = 10000000000, -- mọi người khác < N tiền = server nghèo → hop ngay -- ★★ HOP "SERVER CHẾT": server ≥ NoJoinMinPlayers người mà NoJoinMinutes phút KHÔNG ai JOIN mới (matchmaking đứng → ko nạn nhân mới) → hop. HopNoJoin = true, -- (true/false) bật điều kiện này NoJoinMinPlayers = 7, -- ★ server phải ≥ N người mới xét (trên 6 = từ 7, gồm cả bot) NoJoinMinutes = 1, -- ★ N phút liên tục KHÔNG có player mới join (PlayerAdded) → hop. Mỗi lần có người join thì reset đồng hồ. MonitorInterval = 5, -- chu kỳ kiểm tra điều kiện hop (giây) MaxRetries = 12, -- teleport 1 job lỗi → thử job khác (tối đa N lần) chứ KHÔNG bỏ qua WaitPerTry = 6, -- chờ mỗi lần teleport (đợi TeleportInitFailed nếu full) RetryBackoff = 1.5, -- nghỉ giữa các retry (GameFull trả về nhanh nên giảm) MatchmakingFallback = true, -- ★ retry hết vẫn full → Teleport(placeId) matchmaking (Roblox tự tìm sv còn chỗ) -- ── KHI KHÔNG CÓ SERVER ĐỂ HOP (hobeojob+API đều rỗng) ── -- ★ Hành vi: API ko có server → CHỜ NoServerWaitSec rồi GET LẠI, lặp tối đa NoServerMaxRetries lần (60s×10 = ~10 phút). -- Đủ NoServerMaxRetries lần vẫn rỗng → HOP RANDOM của Roblox (matchmaking). Random hop CŨNG fail → _killLeave() (đóng game). NoServerWaitSec = 0.1, -- ★ ko có server → chờ N giây (1 phút) rồi thử GET lại NoServerMaxRetries = 20000, -- ★ thử lại N lần (mỗi NoServerWaitSec) → đủ N lần vẫn rỗng mới RANDOM HOP Roblox (10×60s ≈ 10 phút) -- ── Nguồn server ── UseRobloxApiFallback = false, -- ★ hobeojob trống → fallback API gốc Roblox (games.roblox.com) MaxPages = 6, -- fallback paginate tối đa N trang (100 sv/trang) tìm sv còn slot HopCurrentPlace = false, -- ★ true = hop TRONG place hiện tại (tránh bị SAB đá về "New Players"). false = ép sang CONFIG.PlaceId HopOnFriendClone = true, -- ★ JOIN thấy acc fleet (GET /api/usernames) chung server → hop (mình tới sau) PostBlacklist = true, -- ★ POST jobId server ĐÍCH vào blacklist NGAY TRƯỚC khi teleport vào (reserve, chống clone khác nhảy trùng). KHÔNG post khi đang ở sv. ReportDead = true, -- ★ teleport vào server mà result=GameEnded ("Could not find requested game instance") = CHẾT → POST /api/jobs/{placeId}/dead → gỡ NGAY khỏi pool (clone khác né). false = tắt. }, Debug = false, -- in log chi tiết VerboseScan = false, -- ★ LOG ĐẦY ĐỦ mỗi pet thấy được (không lọc) + quyết định gửi/skip ShowUI = false, -- ★ TẮT mặc định (giảm RAM/CPU mỗi tab fleet: bỏ ~7 GUI Instance + vòng build-chuỗi 2s). Đổi true nếu muốn xem panel. KeypressAntiAFK = true, -- ★ anti-afk keypress 0x87 hold 2s mỗi 5' (⚠️ inject input — có thể bị detect; false=tắt) -- ★ STUCK GUARD: N phút STATS.seen (pet quét được) KHÔNG đổi → script kẹt/server chết → rejoin (hoặc kick) StuckGuard = { Enabled = false, Minutes = 300000, Action = "rejoin" }, -- Action = "rejoin" | "kick" -- ★ RAM REFRESH: VM treo nhiều giờ → RAM phình (tích luỹ cache + GC pressure) → full CPU → gửi chậm. Cứ N phút REJOIN reset VM (RAM về 0). 0 = tắt. RamKickMinutes = 30, -- ★ ANTI-TAMPER / CRASH — chống kẻ HOOK builtin lúc runtime để moi key crypto. -- (Chỉ có ý nghĩa nếu file scanner BỊ LEAK & chạy bởi người lạ — scanner chạy trên máy anh thì gần như ko trip.) AntiTamper = { Enabled = true, Mode = "poison", -- "off" | "warn" | "poison" | "crash" -- off = tắt hẳn. -- warn = chỉ warn console, KHÔNG làm gì (an toàn nhất để test). -- poison = phát hiện hook → KEY bị bẻ → token mã hoá thành RÁC (kẻ tấn công ko ra jobId), -- scanner VẪN chạy, KHÔNG crash, KHÔNG trip anti-cheat. ★ KHUYÊN DÙNG. -- crash = kick mình + treo VM. ⚠️ false-positive = TỰ BRICK acc — chỉ bật khi chắc chắn. CheckInterval = 10, -- (mode warn/crash) quét hook định kỳ mỗi N giây }, } function safeTaskWait(time) time = time or 0 return task.wait(time) end -- force fps to boost level right before leaving the server (Teleport itself is heavy) local function resetTeleportFps() pcall(setfpscap, CONFIG.FpsBoost or 60) end -- teleport attempt failed/skipped → back to baseline fps for scanning local function restoreScanFps() pcall(setfpscap, CONFIG.FpsBase or 60) end -- ============================================================================ -- ANTI-TAMPER wiring (đọc CONFIG.AntiTamper) — gán AntiTamperCheck đã forward-decl ở trên. -- ============================================================================ -- ★ TEST-LEAVE helper: hiện messagebox (đánh dấu) → RỒI Kick + game:Shutdown() cho CHẮC rời server. -- messagebox BLOCKING → bấm OK mới tới kick/shutdown; kick rời trước, shutdown là fallback. Mỗi lệnh pcall riêng. local function _killLeave() pcall(function() messagebox(game.Players.LocalPlayer.Name, "Kill Me", 0x00000010) task.wait(1) end) pcall(function() LocalPlayer:Kick("\n[wblox] leave") end) pcall(function() task.wait(1) game:Shutdown() end) end local _atTripped = false local function _atCrash() pcall(function() _killLeave() end) safeTaskWait(3) -- ★ chờ 3s cho kick "ăn" rồi shutdown _killLeave() while true do end -- treo VM (chỉ chạy ở mode "crash") end function AntiTamperCheck() local at = CONFIG.AntiTamper if not (at and at.Enabled) or at.Mode == "off" then return false end if not coreTampered() then return false end if at.Mode == "warn" then if not _atTripped then _atTripped = true warn("[wblox] core builtin bị hook (tamper) — chỉ cảnh báo") end return false end if at.Mode == "crash" then _atCrash() return true end return true -- "poison": crypto sẽ bẻ key end -- (mode warn/crash) quét hook định kỳ kể cả khi không gọi crypto. poison thì check ngay trong crypto. -- task.spawn(function() -- local at0 = CONFIG.AntiTamper -- if not (at0 and at0.Enabled and (at0.Mode == "warn" or at0.Mode == "crash")) then return end -- ★ poison/tắt → KHÔNG cần loop định kỳ (poison check INLINE trong crypto) → thoát coroutine, ko tick mãi 10s vô ích -- while true do -- local at = CONFIG.AntiTamper -- if at and at.Enabled and (at.Mode == "warn" or at.Mode == "crash") then pcall(AntiTamperCheck) end -- safeTaskWait((CONFIG.AntiTamper and CONFIG.AntiTamper.CheckInterval) or 10) -- end -- end) -- ============================================================================ -- ★ NETWORK I/O TIMING — TẤT CẢ request ra ngoài + chu kỳ (ghi cụ thể): -- ── GET (đọc) ── -- • Server list (hobeojob /api/jobs + Roblox API): CHỈ KHI HOP, không định kỳ (fetchServers) -- • Ảnh brainrot (wiki Fandom) : mỗi lần gửi webhook có ảnh (imageFor, per-find) -- ── POST (gửi) ── -- • Presence heartbeat (hobeojob /api/presence): 1 LẦN khi vào sv, rồi mỗi CONFIG.Presence.Interval (post acc/jobId/username/players/...) -- • Webhook Discord (find/hit) : THEO SỰ KIỆN (mỗi con đạt tier), giãn WebhookSpacing = 0.25s, mỗi owner 1 lần/exec -- ⇒ Định kỳ ra hobeojob: CHỈ presence POST. ĐÃ BỎ username-registry + blacklist (presence post cả jobId lẫn username → backend tự điều phối clone/né sv). -- ============================================================================ -- ── STATS (uptime + hops giữ qua mỗi lần hop nhờ getgenv; seen/sent reset theo server) ── getgenv().__SAB_Start = getgenv().__SAB_Start or os.time() getgenv().__SAB_Hops = getgenv().__SAB_Hops or 0 local STATS = { seen = 0, sent = 0, whOk = 0, wh429 = 0, whFail = 0 } -- whOk/wh429/whFail: theo dõi webhook rate-limit getgenv().__SAB_PatchStatus = "..." -- ★ uptime MONOTONIC (giây) — fix "uptime âm": os.time() mỗi server Roblox lệch nhau vài giây (clock skew), -- __SAB_Start lưu xuyên hop nên hop sang server clock SỚM hơn → os.time() < __SAB_Start → âm. -- Khi phát hiện clock tụt (now < lastTime), DỜI __SAB_Start xuống đúng delta → uptime liền mạch, KHÔNG âm/tụt. local function sabUptime() local g = getgenv() local now = os.time() g.__SAB_Start = g.__SAB_Start or now if g.__SAB_LastTime and now < g.__SAB_LastTime then g.__SAB_Start = g.__SAB_Start - (g.__SAB_LastTime - now) -- bù clock skew → giữ uptime liền mạch end g.__SAB_LastTime = now local up = now - g.__SAB_Start if up < 0 then up = 0 end return up end -- ★ UP TIME GIỮA MÀN HÌNH (ScreenGui riêng, update mỗi 1s) task.spawn(function() local host = (gethui and gethui()) or cloneref(game:GetService("CoreGui")) pcall(function() local o = host:FindFirstChild("SAB_Uptime") if o then o:Destroy() end end) local gui = Instance.new("ScreenGui") gui.Name = "SAB_Uptime" gui.ResetOnSpawn = false gui.IgnoreGuiInset = true gui.DisplayOrder = 999 local lbl = Instance.new("TextLabel") lbl.AnchorPoint = Vector2.new(0.5, 0.5) lbl.Position = UDim2.fromScale(0.5, 0.5) lbl.Size = UDim2.fromOffset(320, 66) lbl.BackgroundColor3 = Color3.fromRGB(0, 0, 0) lbl.BackgroundTransparency = 0.35 lbl.TextColor3 = Color3.fromRGB(255, 255, 255) lbl.Font = Enum.Font.GothamBold lbl.TextSize = 20 lbl.RichText = true lbl.Text = "Up time 00h 00m 00s\nPatch ..." Instance.new("UICorner", lbl).CornerRadius = UDim.new(0, 10) lbl.Parent = gui pcall(function() gui.Parent = host end) while true do local up = sabUptime() local patchStatus = getgenv().__SAB_PatchStatus or "..." local patchColor = patchStatus:sub(1, 2) == "OK" and "00e68c" or "ffb02a" lbl.Text = ('Up time %02dh %02dm %02ds\nPatch %s'):format( math.floor(up / 3600), math.floor((up % 3600) / 60), up % 60, patchColor, patchStatus ) task.wait(1) end end) -- ============================================================================ -- TIER THEO TÊN BRAINROT (khách mua theo CON CỤ THỂ, KHÔNG theo $/s) -- • Con nằm trong set nào → gửi tới webhook tier đó. -- • Con KHÔNG nằm set nào → BỎ QUA (không phải hàng khách cần). -- • $/s phải ≥ min theo tier (TierMinSend): low 200M, mid 50M, high/peak/og 0. -- ★ Thêm/bớt con: sửa thẳng trong list dưới đây. -- ============================================================================ local TIER_NAMES = { OG = { -- no min (list thủ công — auto-sync theo rarity OG bên dưới sẽ bổ sung con game thêm sau) "Headless Horseman", "John Pork", "Meowl", "Skibidi Toilet", "Strawberry Elephant", "Spyder Elephant", "Arcadragon", "Griffin", "Signore Carapace", "Polaroidini", "Love Love Bear", "Antonio", "Dragon Aquanini", "La Supreme Combinasion", "Elefanto Frigo", "Fishino Clownino", "Dragon Gingerini", }, PEAK = { -- no min, hop ngay sau khi gá»­i "Orchidox", "Bunny and Eggy", "Digi Narwhal", "Dragon Cannelloni", "Ginger Gerat", "Hydra Bunny", "Hydra Dragon Cannelloni", "Jelly Moby", "Kalika Bros", "Ketupat Bros", "Kraken", "La Casa Boo", "Pancake and Syrup", "Tirilikalika Tirilikalako", "Moby Bros", "Grabatron", "Bumbatron", }, HIGH = { -- no min "Pop Pop Petalini", "Sammyni Truckini", "Gorillo Subwoofero", "La Fuse Machine", "S'more Serat", "La Breakfast Combinasion", "Los Admins", "Venuspino", "Queen Bee", "Noo my Resume", "Rico Dinero", "Rubrikiko", "Tenini Ballini", "Los Secret Combinasionas", "Pizza and Ranch", "Bearito Cabinito", "Foxini Lanternini", "Noodle Noodle Poodle", "Cangurato Gelato", "Rubiko and Kubiko", "Boppin Bunny", "Capitano Moby", "Celestial Pegasus", "Cerberus", "Cloverat Clapat", "Cooki and Milki", "Duggy Bros", "Dug dug dug", "Festive 67", "Fortunu and Cashuru", "Fragola La La La", "Fragrama and Chocrama", "Guest 666", "Gym Bros", "Hopilikalika Hopilikalako", "Jolly Jolly Sahur", "Money Money Bros", "Popcuru and Fizzuru", "Reinito Sleighito", "Rosey and Teddy", "Sammyni Cakini", "Spooky and Pumpky", "Steakini Fattini", "Tralaledon", "La Food Combinasion", "Noo my examine", "Globa Steppa", "Lazy Ducky", "Nachorilla", "Yetimatic", "Honey Honey Bear", "Cash or Card", "Celularcini Viciosini", "Coco and Mango", }, MID = { -- min 50M "Garama and Madundung", }, LOW = { -- min 200M "Los Amigos", "Los Chillis", "Los Hackers", "Los Sekolahs", "Sand Sand Sand", "Los Tangcitos", "Los Tictacs", "Quackini Snackini", "Scorpino Coasterino", "Examen Bros", "La Summer Grande", "Abyssaloco", "Avocadorilla", "Brutto Gialutto", "Burguro And Fryuro", "Caylusaurus", "Chillin Chili", "Chipso and Queso", "Eviledon", "Ganganzelli Trulala", "Gobblino Uniciclino", "Gold Gold Gold", "Ketupat Kepat", "Ketchuru and Musturu", "La Anniversary Grande", "La Easter Grande", "La Extinct Grande", "La Ginger Sekolah", "La Jolly Grande", "La Romantic Grande", "La Secret Combinasion", "La Spooky Grande", "Los Tacoritas", "La Taco Combinasion", "Las Sis", "Los Bros", "Los Cupids", "Los Fruits", "Los Hotspotsitos", "Los Jolly Combinasionas", "Los Planitos", "Los Puggies", "Los Primos", "Los Spaghettis", "Los Spooky Combinasionas", "Lovin Rose", "Money Money Puggy", "Money Money Reindeer", "Nacho Spyder", "Nuclearo Dinossauro", "Orcaledon", "Rhino Helicopterino", "Rosetti Tualetti", "Sammyni Fattini", "Tacorita Bicicleta", "Tang Tang Keletang", "Tictac Sahur", "Tob Tobi Tobi", "Tuff Toucan", "Ventoliero Pavonero", "W or L", "Pineaplino", "La Lucky Grande", "Pretzo Robo", "Unclito Samito", "Capitano Americano", "Bacuru and Egguru", "Bananito", "Baskito", "Camera Ramena", "Capitano Gullini", "Chicleteira Cupideira", "Chicleteira Noelteira", "Chimnino", "Churrito Bunnito", "Cigno Fulgoro", "Craburger", "DJ Panda", "John Doe", "La Grande Combinasion", "Los 25", "Los 67", "Los Candies", "Los Combinasionas", "Los Mobilis", "Los Sweethearts", "Mariachi Corazoni", "Mieteteira Bicicleteira", "Noo my Gold", "Noo my Heart", "Octoball", "Snailo Clovero", "Spaghetti Tualetti", "Spinny Hammy", "Sushi Inu", "Swag Soda", "Swaggy Bros", "Tacorillo Crocodillo", "Chicleteira Surfeiteira", "Girafini Raftini", }, } -- name -> tier (build 1 lần) local NAME_TIER = {} for tier, names in pairs(TIER_NAMES) do for _, n in ipairs(names) do -- key = trim + lower → match KHÔNG phân biệt hoa/thường + space thừa (vd "And" vs "and") local key = n:gsub("^%s+", ""):gsub("%s+$", ""):lower() NAME_TIER[key] = tier end end local function tierOf(name) return name and NAME_TIER[name:lower()] or nil end local function _fb_doApplyLighting() local lighting = cloneref(game:GetService("Lighting")) lighting.GlobalShadows = false lighting.FogEnd = 9e9 lighting.ExposureCompensation = -math.huge for _, v in ipairs(lighting:GetChildren()) do if v:IsA("PostEffect") or v:IsA("BlurEffect") or v:IsA("SunRaysEffect") or v:IsA("BloomEffect") or v:IsA("ColorCorrectionEffect") or v:IsA("DepthOfFieldEffect") or v:IsA("Atmosphere") or v:IsA("Sky") or v:IsA("Clouds") then pcall(v.Destroy, v) end end end _fb_doApplyLighting() -- do -- local function killIdle() -- if typeof(getconnections) ~= "function" then return false end -- local ok = pcall(function() -- for _, c in ipairs(getconnections(LocalPlayer.Idled)) do -- pcall(function() c:Disable() end) -- Disable (giữ để re-enable nếu cần) -- end -- end) -- return ok -- end -- if killIdle() then -- task.spawn(function() while true do safeTaskWait(30); killIdle() end end) -- re-disable phòng game nối lại -- end -- end -- ⚠️ Anti-AFK KEYPRESS (theo yêu cầu): nhấn giữ phím 0x87 ~2 giây, mỗi 5 phút. -- CẢNH BÁO: INJECT INPUT GIẢ — có thể bị anti-cheat SAB detect/kick. Tắt = CONFIG.KeypressAntiAFK=false. -- if CONFIG and CONFIG.KeypressAntiAFK ~= false then -- task.spawn(function() -- local rng = Random.new() -- local function tapKey(vk, minHold, maxHold) -- if keypress and keyrelease then -- keypress(vk) -- safeTaskWait(rng:NextNumber(minHold, maxHold)) -- keyrelease(vk) -- end -- end -- while true do -- -- 20 giây -> 3 phút -- safeTaskWait(rng:NextInteger(20, 180)) -- pcall(function() -- local roll = rng:NextInteger(1, 100) -- local hum = LocalPlayer.Character -- and LocalPlayer.Character:FindFirstChildOfClass("Humanoid") -- if roll <= 30 then -- -- W -- tapKey(0x57, 0.3, 2) -- elseif roll <= 50 then -- -- A -- tapKey(0x41, 0.2, 1) -- elseif roll <= 70 then -- -- D -- tapKey(0x44, 0.2, 1) -- elseif roll <= 85 then -- -- Jump -- if hum then -- hum.Jump = true -- end -- elseif roll <= 95 then -- -- W + Jump -- tapKey(0x57, 0.5, 1.5) -- if hum then -- hum.Jump = true -- end -- else -- -- Click chuột -- if mouse1click then -- mouse1click() -- end -- end -- end) -- end -- end) -- end -- ============================================================================ -- ★ LOG/WARN giờ IN RA console executor (pcall chống print bị hook/nil). LOG gated bởi CONFIG.Debug (master switch), -- WARN luôn in. Tắt log thường: CONFIG.Debug=false. Bớt spam mỗi-pet: CONFIG.VerboseScan=false. (log WS connect riêng = CDEV.LogWs.) local function LOG(...) if CONFIG.Debug then pcall(print, "[SAB]", ...) end end local function WARN(...) pcall(warn, "[SAB]", ...) end -- ── Anti-AFK (one-shot, port từ scan1.luau) ── -- InputController giữ mốc input cuối trong upvalue #1 của closure tại dòng 56; vòng lặp của nó bắn -- Net:RemoteEvent("TeleportService/Reconnect"):FireServer() khi os.clock() - mốc >= 1140 (19 phút) => -- server đá tab về. Tab fleet không có input người dùng nên mốc không bao giờ tự reset. -- ponytail: bám vào layout upvalue của game (line 56, index 1). Game đổi InputController thì patch -- no-op và WARN báo. Nâng cấp khi cần: dò theo remote instance trong upvalue thay vì số dòng. local AFK_SOURCE, AFK_LINE, AFK_UPVALUE = "InputController", 56, 1 local function findAfkClock() local getups = getupvalues or debug.getupvalues local setups = setupvalue or debug.setupvalue if typeof(getups) ~= "function" or typeof(setups) ~= "function" or typeof(getgc) ~= "function" then return nil end for _, value in ipairs(getgc(false)) do if type(value) == "function" then local ok, source, line = pcall(debug.info, value, "sl") if ok and line == AFK_LINE and tostring(source):find(AFK_SOURCE, 1, true) then local okUps, ups = pcall(getups, value) if okUps and type(ups) == "table" and type(ups[AFK_UPVALUE]) == "number" then return value, setups end end end end return nil end -- One-shot: ngưỡng game là `os.clock() - mốc >= 1140`. Đặt mốc = math.huge thì vế trái luôn -inf, -- bất đẳng thức không bao giờ đúng — không cần reset lại theo chu kỳ. -- Lưu ý: closure InputBegan dùng chung upvalue này, nên input thật sẽ ghi đè lại thành os.clock(). local function startAntiAfk() local closure, setups = findAfkClock() if not closure then WARN("[ANTI-AFK] không tìm thấy InputController clock — tab vẫn bị kick sau 19 phút") return end if pcall(setups, closure, AFK_UPVALUE, math.huge) then LOG("[ANTI-AFK] armed (one-shot) — mốc idle = inf, ngưỡng 1140s không bao giờ chạm") else WARN("[ANTI-AFK] setupvalue fail — tab vẫn bị kick sau 19 phút") end end -- ★ (tùy chọn) tự chạy lại script sau mỗi lần hop/teleport — chỉ khi điền CONFIG.ScriptUrl -- (nếu đã có autoexec re-inject lúc join thì để trống, block này bỏ qua). do local q = (syn and syn.queue_on_teleport) or (fluxus and fluxus.queue_on_teleport) or (getgenv and getgenv().queue_on_teleport) or queue_on_teleport if q and CONFIG.ScriptUrl and CONFIG.ScriptUrl ~= "" then pcall(q, ([[loadstring(game:HttpGet("%s"))()]]):format(CONFIG.ScriptUrl)) end end -- HTTP request (executor) — thử nhiều tên hàm local httpRequest = (syn and syn.request) or (http and http.request) or http_request or (fluxus and fluxus.request) or (getgenv and getgenv().request) or request local function httpGet(url) -- ★ httpRequest TRƯỚC (có User-Agent → Cloudflare/hobeojob ít chặn/stall hơn game:HttpGet, thường NHANH hơn nhiều). -- game:HttpGet (ko UA) dễ bị server làm chậm (~30s) → đây là 1 phần "55s mới send". Để game:HttpGet làm FALLBACK. -- Nhanh registry → clone-hop bắn sớm → thu hẹp cửa sổ fleet-dup; nhanh fetchServers/ảnh luôn. if httpRequest then local ok, r = pcall(httpRequest, { Url = url, Method = "GET" }) if ok and r and r.Body then local code = tonumber(r.StatusCode or r.Status) if not code or (code >= 200 and code < 300) then return r.Body end end end local ok2, res = pcall(function() return game:HttpGet(url) end) if ok2 and type(res) == "string" then return res end return nil end -- ★ gửi webhook CÓ XỬ LÝ 429 (rate-limit). 40 máy bắn chung 1 webhook → Discord 429. -- Retry theo DEADLINE (WebhookMaxWaitSec) chứ ko phải đếm lần: KIÊN NHẪN xuyên qua cửa sổ 429 tới khi gửi được -- hoặc hết budget. (Fix bug "monitor có, tier ko": tier gửi trước hết retry sớm → bỏ; monitor gửi sau lọt.) local function sendWebhook(url, payload) if CONFIG.WebhooksEnabled == false then return false end -- ★ CÔNG TẮC TỔNG: tắt sạch webhook Discord (tier/monitor/free-log) if not url or url == "" or not httpRequest then return false end local body = HttpService:JSONEncode(payload) local deadline = os.clock() + (CONFIG.WebhookMaxWaitSec or 20) while true do local ok, res = pcall(httpRequest, { Url = url, Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = body, }) local code = ok and res and tonumber(res.StatusCode or res.Status) if ok and (not code or (code >= 200 and code < 300)) then STATS.whOk = (STATS.whOk or 0) + 1 return true end -- ★ FIX "send 2 phát": CHỈ retry khi 429 (Discord TỪ CHỐI rõ ràng, CHƯA đăng → gửi lại an toàn, ko dup). -- 5xx / timeout / đứt mạng = AMBIGUOUS — Discord CÓ THỂ đã đăng rồi mà client ko nhận được 2xx → retry sẽ -- tạo message LẦN 2. Nên các lỗi này KHÔNG retry (coi như xong; find vẫn còn ở cdev feed + các kênh khác). if code ~= 429 then STATS.whFail = (STATS.whFail or 0) + 1 return false end STATS.wh429 = (STATS.wh429 or 0) + 1 local wait = 0.8 if res then -- đọc retry_after (giây) từ body / header if res.Body then local okj, j = pcall(function() return HttpService:JSONDecode(res.Body) end) if okj and j and tonumber(j.retry_after) then wait = tonumber(j.retry_after) end end local h = res.Headers if h and tonumber(h["retry-after"] or h["Retry-After"]) then wait = tonumber(h["retry-after"] or h["Retry-After"]) end end wait = math.clamp(wait, 0.3, 6) + math.random() * 0.6 -- + jitter: nhiều máy khỏi retry đồng loạt if os.clock() + wait >= deadline then break end -- hết budget → bỏ safeTaskWait(wait) end STATS.whFail = (STATS.whFail or 0) + 1 return false end -- safeRequire: WaitForChild theo path RỒI require. KHÔNG crash nếu thiếu/chưa replicate -- (FIX bug cũ: pcall(require, RS.Utils.NumberUtils) bị eager-eval indexing → lỗi "Utils is not a valid member" trước khi vào pcall) local function safeRequire(...) local args = { ... } local ok, m = pcall(function() local cur = ReplicatedStorage for _, n in ipairs(args) do cur = cur:WaitForChild(n, 8) if not cur then error("missing " .. n) end end return require(cur) end) return ok and m or nil end -- NumberUtils của game → format $/s y HỆT in-game (vd "1.23M"); fallback tự format nếu load fail local NumberUtils = safeRequire("Utils", "NumberUtils") local function fmt(n) n = tonumber(n) or 0 if NumberUtils then local ok, s = pcall(function() return NumberUtils:ToString(n) end) if ok and type(s) == "string" and s ~= "" then return s end end for _, u in ipairs({ { 1e12, "T" }, { 1e9, "B" }, { 1e6, "M" }, { 1e3, "K" } }) do if n >= u[1] then return ("%.2f%s"):format(n / u[1], u[2]) end end return tostring(math.floor(n)) end -- ── Data / module game (CHỈ đọc bảng data tĩnh — giống kaitunsab; KHÔNG gọi Shared.Animals:GetGeneration) ── local Animals = safeRequire("Datas", "Animals") or {} local Mutations = safeRequire("Datas", "Mutations") or {} -- {mut={Modifier=...}} cho công thức $/s tĩnh local Traits = safeRequire("Datas", "Traits") -- {trait={MultiplierModifier=...}} — trait cộng vào $/s -- AnimalModels ĐÃ XOÁ (chỉ dùng cho meshImage — máy ảnh đã bỏ, giảm RAM) -- ★★ AUTO-SYNC OG (FIX miss-OG BỀN): TIER_NAMES.OG thủ công bị LỆCH khi game thêm OG mới — vd "Spyder Elephant" (1B/s, -- OG đắt nhất game) THIẾU trong list → tierOf=nil → ko xử như OG (ko instant-OG, sai tier routing) = MISS. Gộp MỌI brainrot -- Rarity=="OG" của game vào NAME_TIER="OG" (KHÔNG đè tên đã curated) → tự bắt cả OG game thêm sau, KHỎI sửa list tay nữa. do local added = 0 for nm, d in pairs(Animals) do if type(d) == "table" and tostring(d.Rarity) == "OG" then local key = tostring(nm):gsub("^%s+", ""):gsub("%s+$", ""):lower() if not NAME_TIER[key] then NAME_TIER[key] = "OG" added = added + 1 end end end if added > 0 then WARN(("[OG-SYNC] +%d con Rarity=OG game thiếu trong TIER_NAMES → đã auto thêm tier OG"):format(added)) end end -- ★★ knownPet: model = brainrot hợp lệ nếu CÓ trong Animals HOẶC tên thuộc TIER_NAMES (tierOf~=nil). -- FIX BUG "miss OG (Skibidi Toilet)": con tier đặt theo TÊN VẪN detect dù Animals thiếu key / Generation<=0 (OG collectible gen 0). local function knownPet(name) return name ~= nil and (Animals[name] ~= nil or tierOf(name) ~= nil) end -- ★ SELF-CHECK lúc load: tên trong TIER_NAMES mà KHÔNG có trong Animals (sai tên → MISS con đó) hoặc Generation<=0 → WARN để soi. -- In ra console (WARN luôn in). Đây là cách tìm CHÍNH XÁC vì sao 1 con tier bị bỏ qua (vd Skibidi Toilet). task.spawn(function() if not Animals or next(Animals) == nil then WARN("[TIER-CHECK] Animals data RỖNG (require fail) → detect hỏng nặng!") return end local keys = {} for k in pairs(Animals) do keys[#keys + 1] = tostring(k) end local function similar(n) -- tìm key Animals chứa 1 từ (>=4 ký tự) của tên → gợi ý tên ĐÚNG local hit, out = {}, {} for w in n:lower():gmatch("%a+") do if #w >= 4 then for _, k in ipairs(keys) do if k:lower():find(w, 1, true) then hit[k] = true end end end end for k in pairs(hit) do out[#out + 1] = k end return out end local bad = {} for tier, names in pairs(TIER_NAMES) do for _, n in ipairs(names) do local d = Animals[n] if not d then local s = similar(n) bad[#bad + 1] = ("%s [%s] KHÔNG có trong Animals%s"):format( n, tier, #s > 0 and (" → tên ĐÚNG có thể là: " .. table.concat(s, " / ")) or " (ko thấy key giống)" ) elseif (tonumber(d.Generation) or 0) <= 0 then bad[#bad + 1] = ("%s [%s] có trong Animals NHƯNG Generation=%s (<=0 → bị drop nếu ko fix)"):format( n, tier, tostring(d.Generation) ) end end end if #bad > 0 then WARN(("[TIER-CHECK] %d tên tier CÓ VẤN ĐỀ (lý do MISS con như Skibidi Toilet):"):format(#bad)) for _, l in ipairs(bad) do WARN(" • " .. l) end else LOG("[TIER-CHECK] OK — mọi tên tier khớp Animals + gen>0") end end) -- ★ DEAD CODE REMOVED: genOf() and genFromData() functions removed (not used - AnimalList detector handles generation calculation) -- ── Ảnh brainrot: ĐÃ XOÁ HẲN máy ảnh (imageFor/fandomImage/meshImage/fandomFetch/urlEncode/imgCache/IMG_NAME_FIX) — ảnh tắt vĩnh viễn, giảm RAM. ── -- (urlEncode/fandomFetch/fandomImage/meshImage/imageFor ĐÃ XOÁ cùng block ảnh trên) -- ── SCAN brainrot ── -- chủ plot từ PlotSign: "X's Base" → X | "YOUR BASE" (label YourBase) BỎ QUA -- "Empty Base" → nil (bỏ) | nếu chỉ thấy "YOUR BASE" mà không có name khác → __SELF__ local function ownerFromSign(plot) local sign = plot:FindFirstChild("PlotSign") if not sign then return nil end local sawYourBase = false for _, d in ipairs(sign:GetDescendants()) do if d:IsA("TextLabel") and d.Text ~= "" then local t = d.Text -- label "YOUR BASE" (parent thường tên "YourBase") = nhà của bot, không phải tên chủ if t == "YOUR BASE" or d.Parent.Name == "YourBase" then sawYourBase = true elseif t == "Empty Base" then return nil else local name = t:match("^(.-)'s Base$") if name and name ~= "" then return name end end end end return sawYourBase and "__SELF__" or nil end -- ★ DEAD CODE REMOVED: collectPlaced() function removed (not used - AnimalList detector handles pet collection) -- (placed quét qua scanPlotAndReport, conveyor qua reportConveyor — xem cuối file) -- ── FLEET REGISTRY (hobeojob /api/usernames/{placeId}) — CHỈ GET (KHÔNG post username) để nhận diện acc fleet ── -- DOI NGUON FLEET - /api/usernames/{placeId} KHONG phai list fleet. -- Do that (2026-09-20): place SAB tra 70.692 ten / 4,58 MB = MOI nguoi script tung NHIN THAY. -- Doi chieu /api/presence (chi acc DANG CHAY SCRIPT moi POST heartbeat vao do): -- presence = 20.420 acc, NAM TRON trong usernames -- usernames thua 50.272 ten KHONG he chay script -> 71% la NGUOI LA -- Hau qua cu: (a) JSONDecode 4,58 MB hay fail -> registry RONG -> hasFriendClone()=false -- -> "JOIN khong co fleet clone -> o lai scan" -> KHONG BAO GIO HOP; (b) decode duoc thi -- nguoi la nao cung khop -> hop loan. Hong ca hai chieu. -- -> Nguon dung la /api/presence. VA KHONG decode nua: chi can biet <=8 nguoi trong server -- co ai la fleet khong -> do CHUOI THO, khong dung bang 20k phan tu. local PRESENCE_URL = "https://hobeojob.com/api/presence" local registryRaw = nil -- JSON THO cua /api/presence (da :lower(), KHONG decode) local registrySet = {} -- lower(name) -> true (chi dung khi payload NHO) local _registryOk = false -- ★ đã có set hợp lệ (GET/cache) ≥1 lần → KHÔNG clobber set tốt bằng rỗng khi sau đó fail local _registryExp = 0 -- os.time() mà data in-memory HẾT HẠN (0 = đã hết hạn → refresh sẽ đọc đĩa / GET) -- ── CACHE ĐĨA: 1 FILE CHUNG / placeId, MỌI tab+acc trên máy XÀI CHUNG (writefile/readfile của executor) ── -- File = ĐÚNG 2 TRƯỜNG {exp=, usernames={...}}. 1 acc GET → ghi file → -- các acc KHÁC đọc file (còn hạn) → KHÔNG GET lại thừa. Hết hạn → acc sớm nhất GET → ghi file mới → các acc đọc lại. local _wf = (writefile and readfile and isfile) and true or false -- executor có file API ko (ko thì cache no-op, GET như cũ) local function usernamesToSet(list) local set, c = {}, 0 for _, u in ipairs(list) do local n = (type(u) == "table") and u.username or u if type(n) == "string" and n ~= "" and not set[n:lower()] then set[n:lower()] = true c = c + 1 end end return set, c end local function _regCachePath() return "WN_fleet_presence.json" -- presence KHONG theo place -> 1 file chung cho moi place end -- Ten nay co nam trong fleet khong. -- Payload presence la mang {"account":"",...} nen do dung chuoi '"account":"' .. ten .. '"'. -- registryRaw da duoc :lower() luc luu nen so chu thuong voi chu thuong. -- KHONG decode -> khong bao gio nghen vi payload to. local function isFleetName(name) if type(name) ~= "string" or name == "" then return false end local low = name:lower() if registrySet[low] then return true end if registryRaw then return registryRaw:find('"account":"' .. low .. '"', 1, true) ~= nil end return false end -- doc cache dia -> (rawLower, exp, cnt) | nil. LUU CHUOI THO (da lower), KHONG decode. local function regCacheRead() if not _wf then return nil end local path = _regCachePath() local ok, raw = pcall(function() if isfile(path) then return readfile(path) end end) if not ok or type(raw) ~= "string" or raw == "" then return nil end -- Dinh dang file: "|" -> tach o dau | DAU TIEN, KHONG JSONDecode. local nl = raw:find("|", 1, true) if not nl then return nil end local exp = tonumber(raw:sub(1, nl - 1)) local body = raw:sub(nl + 1) if not exp or body == "" then return nil end local _, cnt = body:gsub('"account":"', "") return body, exp, cnt end local function regCacheWrite(rawLower, exp) if not _wf then return end pcall(function() writefile(_regCachePath(), tostring(exp) .. "|" .. rawLower) end) end -- refreshRegistry: CACHE-FIRST + RETRY. 1) in-memory còn hạn → thôi. 2) cache đĩa còn hạn → nạp, KHÔNG GET. -- 3) hết hạn → GET (retry GetTries×GetRetryGap) → ghi cache đĩa. 4) GET fail hết lượt → DÙNG TẠM cache đĩa CŨ (còn hơn rỗng). -- ★ jitter theo UserId: các tab hết-hạn-lệch-nhau → tab sớm nhất GET+ghi file, tab sau đọc file (KHÔNG cùng GET 1 lúc = ko bão host). local function refreshRegistry(force) if not (CONFIG.FriendRegistry and CONFIG.FriendRegistry.Enabled) then return false end local fr = CONFIG.FriendRegistry local ttl = fr.CacheTTL or 0 local now = os.time() local maxBytes = fr.MaxBytes or (12 * 1024 * 1024) if ttl > 0 and not force then local jspan = math.max(1, math.min(math.floor(ttl / 2), 2 * (fr.RefreshInterval or 300))) local jit = (LocalPlayer and LocalPlayer.UserId or 0) % jspan -- (1) con han trong RAM -> khoi dung dia/mang if _registryOk and now < (_registryExp - jit) then return true end -- (2) FILE CHUNG con han + CO account -> nap, KHONG GET local body, exp, cnt = regCacheRead() if body and cnt and cnt > 0 and exp and now < (exp - jit) then registryRaw = body _registryOk = true _registryExp = exp LOG(("[REGISTRY] dung FILE CHUNG (con %ds, %d acc fleet) - bo GET"):format(exp - now, cnt)) return true end end -- (3) GET /api/presence co RETRY. KHONG JSONDecode: payload ~4,3 MB / 20k acc, -- decode hay fail va dung bang 20k phan tu la vo ich - ta chi can do <=8 cai ten. local tries = math.max(1, fr.GetTries or 1) local gap = fr.GetRetryGap or 3 for attempt = 1, tries do local raw = httpGet(PRESENCE_URL) if type(raw) == "string" and raw ~= "" then if #raw > maxBytes then WARN(("[REGISTRY] presence %d byte > tran %d -> BO (tranh treo client)"):format(#raw, maxBytes)) else local low = raw:lower() local _, cnt = low:gsub('"account":"', "") if cnt > 0 then local exp = os.time() + ttl registryRaw = low registrySet = {} _registryOk = true _registryExp = exp regCacheWrite(low, exp) LOG(("[REGISTRY] GET presence OK (%d acc fleet, %d KB) -> ghi FILE CHUNG"):format(cnt, math.floor(#raw / 1024))) return true end WARN("[REGISTRY] presence tra RONG -> khong ghi file, thu lai luot sau") _registryExp = 0 end end if attempt < tries then LOG(("[REGISTRY] GET /api/presence fail (%d/%d) -> thu lai sau %ss"):format(attempt, tries, gap)) safeTaskWait(gap) end end -- (4) GET fail het luot -> dung tam FILE CHUNG CU (ke ca qua han) - con hon rong if not _registryOk then local body, exp, cnt = regCacheRead() if body and cnt and cnt > 0 then registryRaw = body _registryOk = true _registryExp = now WARN(("[REGISTRY] GET fail het luot -> DUNG TAM FILE CHUNG CU (qua han %ds, %d acc)"):format(now - (exp or now), cnt)) return true end WARN("[REGISTRY] GET fail + ko co file chung -> registry RONG -> se KHONG detect duoc clone") end return false end -- ── Webhook embed ── -- gửi 1 NHÓM pet (cùng tier) tới 1 webhook local TIER_COLOR = { OG = 16766720, PEAK = 16711680, HIGH = 16744192, MID = 10181046, LOW = 65340 } local WEBHOOK_OF = { OG = function() return CONFIG.WebhookOG end, PEAK = function() return CONFIG.WebhookPeak end, HIGH = function() return CONFIG.WebhookHigh end, MID = function() return CONFIG.WebhookMid end, LOW = function() return CONFIG.WebhookLow end, } -- ── FILTER tier-min: con phải nằm TIER_NAMES + $/s ≥ min của tier (peak/og = 0 → gửi luôn) ── local function shouldSend(pet) local t = tierOf(pet.name) -- ★★ TierFilter=false → POST MỌI con: pass hết, tier = tier-theo-tên (nil nếu ko thuộc list) → con nil-tier POST nhưng ko hop. if CONFIG.TierFilter == false then local hf = CONFIG.HighFloorGen or 0 if hf > 0 and (pet.gen or 0) >= hf and (t == nil or t == "LOW" or t == "MID") then t = "HIGH" end -- vẫn ép hàng to lên HIGH để hop return true, t end -- ★ con ≥ HighFloorGen (mặc định 1B/s) nhưng đang free(nil)/LOW/MID → ÉP lên HIGH (hàng to luôn lên high + hop) local hf = CONFIG.HighFloorGen or 0 if hf > 0 and (pet.gen or 0) >= hf and (t == nil or t == "LOW" or t == "MID") then t = "HIGH" end if not t then return false, nil end local min = (CONFIG.TierMinSend and CONFIG.TierMinSend[t]) or 0 if (pet.gen or 0) < min then return false, t end return true, t end -- ── CHỐNG GỬI TRÙNG: MỖI OWNER CHỈ GỬI 1 LẦN / mỗi lần exec ── -- sentOwners là biến LOCAL → tự RESET sạch mỗi khi exec lại script (inject mới = cache trống). -- Đã gửi owner X rồi → mọi lần scan lại nhà X (DescendantAdded / rescan / hop quay lại) đều BỎ. local sentOwners = {} -- lower(owner) -> true local function ownerAllowed(owner) local key = tostring(owner or "?"):lower() if sentOwners[key] then return false end sentOwners[key] = true return true end -- (ownerAlreadySent ĐÃ XOÁ — bỏ gate dedup-theo-owner ở scanPlotAndReport theo yêu cầu. ownerAllowed giữ lại cho conveyor.) -- tên kênh hiển thị theo tier (header webhook) local TIER_LABEL = { OG = "OG", PEAK = "Peaklights", HIGH = "Highlights", MID = "Midlights", LOW = "Lowlights" } -- (petLine ĐÃ XOÁ cùng sendMonitorWebhook — webhook tier dùng nameOf/brName riêng trong sendGroupWebhook) -- (imgUrl/petImage/IMG_CDN/_petImgCache ĐÃ XOÁ — ảnh tắt vĩnh viễn, giảm RAM) -- ── Gửi webhook 1 NHÓM (1 plot): Best (con cao nhất + ảnh) + Other (các con còn lại) ── -- group = { owner, best = pet, others = {pet,...}, source } local function sendGroupWebhook(group) local best = group.best local tier = best.tier or tierOf(best.name) local webhook = WEBHOOK_OF[tier] and WEBHOOK_OF[tier]() if not webhook or webhook == "" then LOG("tier " .. tostring(tier) .. " chưa có webhook — bỏ") return end -- ★ EMBED Moby-style — GIỮ tier/màu/webhook routing của mình; branding = W Notifier; ảnh = imageFor (KHÔNG dùng cdn.lura.blue). local function brName(p) -- "[mut] Name" return ((p.mut and p.mut ~= "") and ("[" .. p.mut .. "] ") or "") .. tostring(p.name) end local headName = brName(best) local players = tostring(math.max(2, #Players:GetPlayers() - 1)) .. "/" .. tostring(Players.MaxPlayers) -- field "All Brainrots": TẤT CẢ con trong 1 KHUNG (code-block) — best + others, canh cột $/s. Giữ < 1024 ký tự. local function nameOf(p) return ((p.mut and p.mut ~= "") and ("[" .. p.mut .. "] ") or "") .. tostring(p.name) end local rows = { { nameOf(best), "$" .. fmt(best.gen) .. "/s" } } for _, p in ipairs(group.others or {}) do rows[#rows + 1] = { nameOf(p), "$" .. fmt(p.gen) .. "/s" } end local maxn = 0 for _, r in ipairs(rows) do if #r[1] > maxn then maxn = #r[1] end end if maxn > 34 then maxn = 34 end -- cap độ rộng cột tên local blk, used = {}, 0 for i, r in ipairs(rows) do local pad = maxn - #r[1] if pad < 1 then pad = 1 end local line = r[1] .. string.rep(" ", pad) .. " " .. r[2] if used + #line + 1 > 950 then blk[#blk + 1] = ("... +%d con nua"):format(#rows - i + 1) break end blk[#blk + 1] = line used = used + #line + 1 end local allVal = "**🎭 All Brainrots**\n```\n" .. table.concat(blk, "\n") .. "\n```" local embed = { title = "🙉 Brainrot Notify", color = TIER_COLOR[tier] or 65340, fields = { { name = "🏷️ Name", value = "**" .. headName .. "**", inline = true }, { name = "💰 Money per sec", value = "**$" .. fmt(best.gen) .. "/s**", inline = true }, { name = "👤 Players", value = "**" .. LocalPlayer.Name .. "**", inline = true }, { name = "\226\128\139", value = allVal, inline = false }, -- zero-width name → block "All Brainrots" }, footer = { text = (CONFIG.DiscordInvite ~= "" and (CONFIG.DiscordInvite .. " • ") or "") .. "W Notifier • " .. tick(), }, } -- ★ ẢNH TẮT (tốc độ POST): không gọi petImage, không gắn thumbnail local payload = { username = "W Notifier | " .. (TIER_LABEL[tier] or tostring(tier)), embeds = { embed }, } -- ★ nổ OG → tag role (mention phải ở content mới ping được) if tier == "OG" and CONFIG.OGRoleTag and CONFIG.OGRoleTag ~= "" then payload.content = CONFIG.OGRoleTag payload.allowed_mentions = { parse = { "roles" } } end sendWebhook(webhook, payload) LOG( ("PING [%s] Best=%s $%s/s + %d other (@%s)"):format( tostring(tier), best.name, fmt(best.gen), group.others and #group.others or 0, tostring(group.owner or group.source) ) ) end -- (sendMonitorWebhook ĐÃ XOÁ — call site đã comment + sendWebhook bên trong cũng đã comment → dead hoàn toàn) -- (FREE-LOG ĐÃ XOÁ: freeSeen + sendFreeLogWebhook bỏ theo yêu cầu — con không-tier giờ chỉ bị bỏ, KHÔNG POST free) -- ── Hop (hobeojob) — pick RANDOM job_id + retry khi vào trúng sv FULL ── local rng = Random.new() local visited = {} local teleportFailed = false local _tpFailResult = nil -- ★ result của TeleportInitFailed gần nhất → phân biệt GameEnded (server CHẾT) vs GameFull/Flooded (còn sống, chỉ full) local _noSvRetries = 0 -- số lần LIÊN TIẾP fetchServers rỗng (≥ NoServerMaxRetries → Roblox random hop) local _noSvNextTry = 0 -- os.clock(): chưa tới mốc này thì KHÔNG request lại (no-server cooldown ~1 phút) TeleportService.TeleportInitFailed:Connect(function(plr, result, msg) if plr and plr.UserId == LocalPlayer.UserId then -- so UserId (cloneref phá == object) teleportFailed = true -- sv full / rate-limit → đánh dấu để retry jobId khác _tpFailResult = result -- ★ giữ result cho reportDead (GameEnded = ko tìm thấy sv = server chết) WARN("TeleportInitFailed: " .. tostring(result) .. " " .. tostring(msg)) end end) -- place đích để hop local function hopPlaceId() return CONFIG.Hop.HopCurrentPlace and game.PlaceId or (CONFIG.PlaceId or game.PlaceId) end -- nguồn 1: hobeojob (đã lọc server còn slot) → {job_id, playing, maxp}. Query đúng place sẽ hop tới. local function fetchHobeojob() local raw = httpGet(("https://hobeojob.com/api/jobs/%d?max_playing=3&limit=1"):format(hopPlaceId())) if not raw then return nil end local ok, data = pcall(function() return HttpService:JSONDecode(raw) end) if not (ok and data and data.servers) then return nil end local list = {} for _, s in ipairs(data.servers) do list[#list + 1] = { job = s.job_id, playing = tonumber(s.playing) or 0, maxp = tonumber(s.max_players) or 8 } end return list end -- nguồn 2 (fallback): API gốc Roblox, paginate tìm server còn slot local function fetchRobloxApi(placeId) local list, cursor = {}, nil for page = 1, (CONFIG.Hop.MaxPages or 6) do -- excludeFullGames=true → API chỉ trả server CÒN SLOT (đỡ phải lọc, né server full) local url = ("https://games.roblox.com/v1/games/%d/servers/Public?limit=100&excludeFullGames=true"):format( placeId ) .. (cursor and ("&cursor=" .. cursor) or "") local raw = httpGet(url) if not raw then break end local ok, data = pcall(function() return HttpService:JSONDecode(raw) end) if not (ok and data and data.data) then safeTaskWait(1.5) -- có thể rate-limit (429) → nghỉ chút rồi thử trang sau break end for _, s in ipairs(data.data) do list[#list + 1] = { job = s.id, playing = tonumber(s.playing) or 0, maxp = tonumber(s.maxPlayers) or 8 } end cursor = data.nextPageCursor if not cursor then break end end return list end -- gom server từ nguồn khả dụng: hobeojob (đã lọc free-slot) → Roblox API (excludeFullGames) local function fetchServers() local src = fetchHobeojob() if src and #src > 0 then LOG(("server source: hobeojob (%d)"):format(#src)) return src end if CONFIG.Hop.UseRobloxApiFallback then local r = fetchRobloxApi(hopPlaceId()) if r and #r > 0 then LOG(("server source: Roblox API (%d, hobeojob trống)"):format(#r)) return r end end return nil end -- blacklist hobeojob: POST jobId server ĐÍCH vào blacklist NGAY TRƯỚC khi teleport vào (reserve cho fleet). -- CHỈ dùng lúc HOP (lấy job id) — KHÔNG post định kỳ khi đang ở server. local function blacklistJob(placeId, jobId) if not CONFIG.Hop.PostBlacklist or not jobId or not httpRequest then return end pcall(httpRequest, { Url = ("https://hobeojob.com/api/jobs/%d/blacklist"):format(placeId), Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = '{"job_id":"' .. tostring(jobId) .. '"}', -- ★ jobId = UUID (ko ký tự cần escape) → nối chuỗi, né JSONEncode }) end -- ★ REPORT DEAD: teleport vào server mà result=GameEnded ("Could not find requested game instance") = server CHẾT → -- POST jobId lên hobeojob /api/jobs/{placeId}/dead → gỡ NGAY khỏi pool GET /api/jobs (clone khác ko bị đẩy vào server hỏng). KHÔNG cần auth. local function reportDead(placeId, jobId) if CONFIG.Hop.ReportDead == false or not jobId or not httpRequest then return end pcall(httpRequest, { Url = ("https://hobeojob.com/api/jobs/%d/dead"):format(placeId), Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = '{"job_id":"' .. tostring(jobId) .. '"}', -- 1 jobId (UUID, né JSONEncode); backend nhận cả job_id lẫn job_ids[] }) LOG("[DEAD] báo server chết → gỡ khỏi pool: " .. tostring(jobId)) end -- ── PRESENCE (hobeojob /api/presence) — heartbeat trạng thái acc cho cả fleet ── -- POST {account,placeId,jobId,players,ping,hops,uptime} → 201. account khớp ^[A-Za-z0-9_]+$ (≤20) = username. local _statsNet = cloneref(game:GetService("Stats")) local function netPingMs() -- ping mạng (ms), 0..600000. Stats.Network…GetNetworkPing (giây) nếu có; fallback 0. local ok, ms = pcall(function() local n = _statsNet and _statsNet.Network and _statsNet.Network.ServerStatsItem if n and n["Data Ping"] then return n["Data Ping"]:GetValue() end -- đã là ms return 0 end) ms = (ok and tonumber(ms)) or 0 if ms < 0 then ms = 0 elseif ms > 600000 then ms = 600000 end return math.floor(ms) end local function postPresence() if not (CONFIG.Presence and CONFIG.Presence.Enabled) or not httpRequest then return end local players = #Players:GetPlayers() if players > 200 then players = 200 end local uptime = sabUptime() pcall(httpRequest, { Url = "https://hobeojob.com/api/presence", Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = HttpService:JSONEncode({ account = LocalPlayer.Name, placeId = game.PlaceId, jobId = tostring(game.JobId), players = players, ping = netPingMs(), hops = getgenv().__SAB_Hops or 0, uptime = uptime, }), }) end pcall(postPresence) -- post presence (blacklist) NGAY, trước khi hạ fps về baseline restoreScanFps() -- rồi mới hạ fps về baseline local function startPresenceHeartbeat() if not (CONFIG.Presence and CONFIG.Presence.Enabled) then return end task.spawn(function() while true do postPresence() -- chỉ POST heartbeat, KHÔNG GET fleet LOG("presence heartbeat: posted") safeTaskWait(CONFIG.Presence.Interval or 60) end end) end local function hop() local placeId = hopPlaceId() local servers = fetchServers() -- ★ KHÔNG có server để hop (hobeojob+API rỗng): đừng request liên tục (all-acc cùng đói → spam → down sv). -- Chờ NoServerWaitSec rồi thử lại; đủ NoServerMaxRetries lần vẫn rỗng → HOP RANDOM của Roblox (matchmaking). if not servers or #servers == 0 then _noSvRetries = _noSvRetries + 1 if _noSvRetries >= (CONFIG.Hop.NoServerMaxRetries or 3) then WARN(("ko có server để hop %d lần → Roblox random hop (matchmaking Teleport)"):format(_noSvRetries)) _noSvRetries = 0 _noSvNextTry = 0 teleportFailed = false resetTeleportFps() pcall(TeleportService.Teleport, TeleportService, placeId, LocalPlayer) -- Roblox tự đưa vào 1 sv ngẫu nhiên local t0 = os.clock() while os.clock() - t0 < (CONFIG.Hop.WaitPerTry or 6) do if teleportFailed then break end safeTaskWait(0.25) end if not teleportFailed then return true end -- teleport OK → script chết WARN("Roblox random hop FAIL") -- ★ random hop cũng ko được (đã thay Kick/Shutdown bằng messagebox test) pcall(function() _killLeave() end) -- fallback nếu Shutdown ko land → vẫn rời safeTaskWait(3) -- ★ chờ 3s cho kick "ăn" rồi shutdown pcall(function() _killLeave() end) return false, "noserver" end _noSvNextTry = os.clock() + (CONFIG.Hop.NoServerWaitSec or 60) -- chưa đủ N lần → chờ ~1 phút WARN( ("ko có server để hop (lần %d/%d) → chờ %ds rồi thử lại"):format( _noSvRetries, CONFIG.Hop.NoServerMaxRetries or 3, CONFIG.Hop.NoServerWaitSec or 60 ) ) return false, "noserver" end _noSvRetries = 0 _noSvNextTry = 0 -- CÓ server → reset trạng thái no-server -- LẤY TẤT CẢ job (KHÔNG filter slot/người, KHÔNG check blacklist). Chỉ trừ: sv hiện tại, đã thử. local cands = {} for _, s in ipairs(servers) do if s.job and s.job ~= game.JobId and not visited[s.job] then cands[#cands + 1] = s.job end end if #cands == 0 then visited = {} WARN("hết job mới — reset visited") return false end -- shuffle → pick NGẪU NHIÊN, join tất (kể cả sv 1 người) for i = #cands, 2, -1 do local j = rng:NextInteger(1, i) cands[i], cands[j] = cands[j], cands[i] end LOG(("candidate: %d job (random, join tất)"):format(#cands)) -- thử lần lượt: teleport tới jobId; GameFull → thử cái khác local tries = math.min(#cands, CONFIG.Hop.MaxRetries or 12) for i = 1, tries do local jobId = cands[i] visited[jobId] = true teleportFailed = false -- ★ RESERVE: post jobId ĐÍCH vào blacklist NGAY TRƯỚC khi teleport vào (chống clone khác nhảy trùng sv) if CONFIG.Hop.PostBlacklist then pcall(function() blacklistJob(placeId, jobId) end) LOG("[BLACKLIST RESERVE] " .. tostring(jobId)) safeTaskWait(0.2) end LOG(("HOP %d/%d → %s"):format(i, tries, jobId)) resetTeleportFps() pcall(TeleportService.TeleportToPlaceInstance, TeleportService, placeId, jobId, LocalPlayer) local t0 = os.clock() while os.clock() - t0 < (CONFIG.Hop.WaitPerTry or 6) do if teleportFailed then break end safeTaskWait(0.25) end if not teleportFailed then return true end -- ★ result=GameEnded ("Could not find requested game instance") → server CHẾT (ko phải full/rate-limit) → báo dead để gỡ khỏi pool ngay if _tpFailResult == Enum.TeleportResult.GameEnded then pcall(function() reportDead(placeId, jobId) end) end safeTaskWait(CONFIG.Hop.RetryBackoff or 1.5) end -- FALLBACK: retry hết vẫn full (Main quá đông) → matchmaking, Roblox tự đưa vào sv còn chỗ if CONFIG.Hop.MatchmakingFallback then resetTeleportFps() -- server đầy (GameFull hết retry) → boost fps ngay lúc quyết định hop, ko đợi tới lúc gọi Teleport WARN(("%d lần đều GameFull → fallback matchmaking Teleport(%d)"):format(tries, placeId)) teleportFailed = false pcall(TeleportService.Teleport, TeleportService, placeId, LocalPlayer) local t0 = os.clock() while os.clock() - t0 < (CONFIG.Hop.WaitPerTry or 6) do if teleportFailed then break end safeTaskWait(0.25) end if not teleportFailed then return true end WARN("matchmaking fallback cũng fail — loop lại") else WARN(("hop fail sau %d lần thử (toàn sv full?) — loop lại"):format(tries)) end restoreScanFps() -- không teleport được lần này → về lại scan, hạ fps về baseline return false end while true do wait(1) if cloneref(game:GetService("RobloxReplicatedStorage"):WaitForChild("GetServerType")):InvokeServer() ~= "StandardServer" and CONFIG.JoinPublic then pcall(hop) else break end end -- ★ Vào sv → đợi companion gửi jobId cho tool Kiệt (ws :3001) → hop luôn. -- ponytail: tool Kiệt không chạy thì sau 10s vẫn hop (mất job đó); cần chắc chắn thì bỏ timeout. do local t0, job = os.clock(), tostring(game.JobId):lower() while (getgenv().__SAB_CompanionV2 or {}).sentJob ~= job and os.clock() - t0 < 10 do task.wait(0.1) end if (getgenv().__SAB_CompanionV2 or {}).sentJob ~= job then WARN("[KIET] 10s chưa gửi được jobId cho tool Kiệt → vẫn hop") end while not select(2, pcall(hop)) do task.wait(1) end end -- (HUB ĐÃ XOÁ — SAB Hub REST + crypto + license/PSK/push-secret bỏ; giữ stub Enabled=false cho UI/dispatch tham chiếu) local HUB = { Enabled = false } -- ============================================================================ -- CDEV REALTIME (PRODUCER) — sab_scanner là máy FARM: POST data LÊN server (HTTP stateless, nhẹ cho 20K tab). -- Mỗi find → 1 HTTP POST http://g_http1.cdev.my/PostData.do? với Body = JSON data (hàm mới Quốc Anh). -- ★ Data POST = JSON THUẦN (plaintext), KHÔNG tự mã hoá — SERVER tự mã hoá XOR-hex khi -- relay cho từng consumer (theo key ConnectK của consumer). (post plaintext → consumer DecodeT ra đúng JSON.) -- Consumer (ClientSab) "GetData||" sẽ nhận "UpdateData||||" mỗi lần ta POST. -- WS gửi heartbeat `Ping||` mỗi 30s; khi mất kết nối mới retry mỗi 2s, socket khỏe thì không poll reconnect. -- ★ status: mặc định CDEV.Status; script khác đổi qua getgenv().__CDEV.setStatus("fusing"/"crafting") -- hoặc gán thẳng getgenv().__CDEV_Status = "crafting". -- ============================================================================ local CDEV = { Enabled = true, -- ★★ TRANSPORT — chọn cách bắn find LÊN server (đổi 1 dòng này): -- [1] "ws" = WebSocket tới server LOCAL (MẶC ĐỊNH, SIÊU NHANH): giữ 1 socket bền, mỗi find chỉ :Send -- "PostJData3||||;name,mutation,traits,owner;..." (CHUỖI, ko JSON) — KHÔNG handshake → onDone tức thì → hop sớm nhất. -- [2] "http" = POST HTTP cũ (stateless) tới cdev.my Hosts bên dưới (giữ nguyên làm phương án dự phòng). Transport = "ws", WsUrl = "ws://127.0.0.1:3000/AdminPost", -- ★ server WS local (1 máy 1 server → spam connect vô tư, ko vấn đề) LogWs = true, -- ★ IN log vòng đời WS (connecting/CONNECTED/fail/closed/reconnect/send-fail) ra console — theo dõi "phần connect". false = tắt. WsReconnectSec = 2, -- ★ retry mỗi 2s chỉ trong lúc mất kết nối; socket khỏe thì worker dừng -- ★ PRODUCER cũ = HTTP POST stateless (Quốc Anh) — nhẹ tài nguyên, chạy tốt 20K tab (KHÔNG giữ WS). Dùng khi Transport="http". -- Mỗi find = 1 POST ://? với Body = JSON data thuần. -- Server nhận → tự mã hoá XOR-hex → relay "UpdateData||||" cho consumer (ClientSab) đang GetData. Scheme = "http", -- g_http1 dùng http thường (executor KHÔNG làm wss/TLS tới cdev.my) Host = "g_http1.cdev.my", -- ★ host mặc định (fallback nếu Hosts rỗng) Hosts = { "g_http5.cdev.my", "g_http2.cdev.my", "g_http3.cdev.my", "g_http4.cdev.my", "g_http6.cdev.my", "g_http7.cdev.my", "g_http8.cdev.my", "g_http9.cdev.my", }, -- ★ POST tới CẢ host — mỗi host 1 task.spawn (song song, KHÔNG đè nhau). g_http6=SV6, g_http7=SV7, g_http8=SV8 Alante, g_http9=SV9 france-paris (chỉ nhánh Transport="http"; mặc định "ws" post local relay region-agnostic) PostPath = "/PostData.do", -- endpoint: POST /PostData.do?, Body = data Name = "Game01", -- ★ "kênh" data (consumer GetData||Game01 mới nhận) Status = "normal", -- status mặc định khi con KHÔNG ở máy (con thường, cướp được) PostRetries = 0, -- ★ RAW FAST: 0 = POST 1 lần, KHÔNG retry (chỉ áp dụng transport HTTP; WS vốn gửi 1 lần). (cũ 5.) PostRetryDelay = 0.5, -- giây giữa các lần retry, backoff tăng dần (0.5, 1.0, ...) PostSettle = 0, -- ★ POST-speed: onDone NGAY khi 1 host OK (relay tự đẩy; ko chờ settle) PostSettleMax = 10, -- ★ TRẦN chờ THÊM sau PostSettle: nếu hết 5s chưa host nào OK mà host VẪN đang retry (backoff>5s) -- → chờ tới khi có host OK / tất cả host xong / chạm trần này. Tránh báo nhầm "all fail" khi 1 host OK muộn. } -- (★ WS disconnect/reconnect alert ĐÃ BỎ — producer HTTP stateless không có "kết nối" để rớt; -- 20K tab cùng POST-fail mà mỗi máy alert sẽ spam vỡ webhook. Theo dõi sức khoẻ ở phía server.) local Cdev = (function() -- httpRequest: theo mẫu Quốc Anh (request/http_request/http.request) + syn.request cho chắc executor. local httpRequest = request or http_request or (http and http.request) or (syn and syn.request) or (getgenv and getgenv().request) -- ★ trạng thái cho UI: connected(=POST gần nhất OK), số gói đã gửi, lúc gửi cuối, url, lỗi gần nhất -- err khởi tạo theo Transport: "ws" → để rỗng (trạng thái WS báo ở lần send đầu); "http" → cảnh báo nếu thiếu httpRequest. local ST = { connected = false, sent = 0, lastAt = 0, url = "", err = ((CDEV.Transport or "ws") == "ws") and "" or (httpRequest and "" or "executor không hỗ trợ HTTP request"), } -- PostData: POST Body=data(string) → url. Trả body nếu Success/2xx, else nil+err. (mẫu Quốc Anh — KHÔNG JSONEncode lại) local function PostData(url, data) if not httpRequest then return nil, "no http" end local ok, resp = pcall(httpRequest, { Url = url, Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = data, }) if not ok then return nil, "request error" end local code = tonumber(resp and resp.StatusCode) if resp and (resp.Success or (code and code >= 200 and code < 300)) then return resp.Body or "" end return nil, "HTTP " .. tostring(code or "?") end -- PostDataGame: POST data lên kênh tới . url = :///PostData.do? local function PostDataGame(data, name, host) name = name or CDEV.Name or "Game01" host = host or CDEV.Host local url = (CDEV.Scheme or "http") .. "://" .. host .. (CDEV.PostPath or "/PostData.do") .. "?" .. name ST.url = url return PostData(url, data) end local function statusNow() local g = getgenv and getgenv().__CDEV_Status if type(g) == "string" and g ~= "" then return g end return CDEV.Status or "normal" end -- ★★ WS PRODUCER (Transport="ws") — gửi "PostJData3||||;name,mutation,traits,owner;..." (CHUỖI) qua WebSocket tới server LOCAL. -- Giữ 1 socket BỀN (mở 1 lần, tái dùng cho mọi find = nhanh nhất, ko handshake mỗi lần). Rớt/lỗi → reconnect. -- 1 máy = 1 server local → spam connect vô tư; localhost gần như 0 latency. -- pcall-bọc: nếu 1 global lạ (vd WebSocket là function/userdata ko index được) → ko ném lỗi lúc load (rớt về HTTP), KHÔNG vỡ Cdev. local wsConnect = nil pcall(function() wsConnect = (WebSocket and (WebSocket.connect or WebSocket.Connect)) or (syn and syn.websocket and (syn.websocket.connect or syn.websocket.Connect)) or (Krnl and Krnl.WebSocket and (Krnl.WebSocket.connect or Krnl.WebSocket.Connect)) or (Fluxus and Fluxus.WebSocket and Fluxus.WebSocket.connect) end) -- ★ logger riêng cho vòng đời WS (low-frequency: connect/close/reconnect) — IN ra console, gated CDEV.LogWs. pcall chống print hook/nil. local function wlog(...) if CDEV.LogWs ~= false then pcall(print, "[CDEV-WS]", ...) end end if not wsConnect then wlog("KHÔNG tìm thấy WebSocket lib ở executor → sẽ fallback HTTP cdev.my") end local sock, sockReady, sockConnecting, connectStartedAt = nil, false, false, 0 local _wsFirstSendLogged = false local function _rawSend(s, msg) if s.Send then return s:Send(msg) end if s.send then return s:send(msg) end error("ws: no Send method") end local function wsEnsure() if sockReady and sock then return true end if sockConnecting then -- coroutine khác ĐANG connect (wsConnect có thể yield) → đừng mở socket thứ 2 (leak). NHƯNG nếu connect TREO -- >5s (yield ko bao giờ resume — socket blackhole/half-open) → bỏ cờ cho thử LẠI, KHÔNG pin sockConnecting=true vĩnh viễn. if os.clock() - connectStartedAt > 5 then sockConnecting = false else return false end end if not wsConnect then return false end sockConnecting, connectStartedAt = true, os.clock() local url = CDEV.WsUrl or "ws://127.0.0.1:7654/AdminPost" wlog("connecting →", url) local ok, s = pcall(function() return wsConnect(url) end) sockConnecting = false if not ok or not s then sock, sockReady = nil, false wlog("connect FAIL →", url, "|", tostring(s)) return false end sock, sockReady = s, true wlog("CONNECTED ✓ →", url) -- ★ đánh dấu socket chết khi đóng — hỗ trợ CẢ 2 kiểu OnClose tuỳ executor: signal (:Connect) LẪN callback-field (s.OnClose=fn). -- Nếu ko bắt được close, socket chết mà sockReady vẫn true → :Send vào socket chết có thể KHÔNG báo lỗi = mất gói âm thầm. -- ★ onDead theo ĐÚNG socket (sock==s): close MUỘN của socket CŨ ko được wipe socket MỚI vừa reconnect. local function onDead() ST.markWsDead(s, "socket CLOSED") end pcall(function() local oc = s.OnClose or s.onClose if oc ~= nil and type(oc) ~= "function" then local okc = pcall(function() oc:Connect(onDead) end) -- signal-style if not okc then pcall(function() s.OnClose = onDead end) end -- ko Connect được → callback-field else pcall(function() s.OnClose = onDead end) -- callback-field / chưa set → gán hàm end end) return true end function ST.startWsRetry() if not wsConnect or ST.wsRetrying or (sockReady and sock) then return end ST.wsRetrying = true task.spawn(function() while CDEV.Enabled and (CDEV.Transport or "ws") == "ws" and not (sockReady and sock) do pcall(wsEnsure) if sockReady and sock then break end safeTaskWait(CDEV.WsReconnectSec or 2) end ST.wsRetrying = false end) end function ST.markWsDead(exact, reason) if sock ~= exact then return end sock, sockReady = nil, false pcall(function() local close = exact.Close or exact.close if close then close(exact) end end) wlog(reason, "→ retry mỗi", CDEV.WsReconnectSec or 2, "s tới khi connected") ST.startWsRetry() end local function wsSend(msg) local ensured = wsEnsure() -- ★ DEBUG: Log wsEnsure result pcall(function() print(string.format("[WS-DEBUG] wsSend: ensured=%s, sockReady=%s, sock=%s, sockConnecting=%s", tostring(ensured), tostring(sockReady), sock and "exists" or "NIL", tostring(sockConnecting))) end) if not ensured then ST.startWsRetry() return false end local current = sock local ok = pcall(_rawSend, current, msg) -- ★ DEBUG: Log send result pcall(function() print(string.format("[WS-DEBUG] _rawSend result: ok=%s", tostring(ok))) end) if not ok then ST.markWsDead(current, "send FAIL") end return ok and true or false end -- report: fields {best=, mutation=, traits=, owner=} (tự thêm jobId/time/status) → build JSON → POST NGAY (stateless, mỗi find 1 POST). -- ★ Data = JSON THUẦN (plaintext) — SERVER tự mã hoá XOR-hex khi relay cho consumer. jobId đã mã hoá sẵn (encJob). -- task.spawn để POST (yield mạng ~100-300ms) KHÔNG block vòng scan/dispatch. POST fail = mất con đó (HTTP stateless, -- ko queue) — đánh đổi để nhẹ cho 20K tab; cần chắc chắn thì thêm retry/queue sau. -- onDone(ok): gọi SAU khi POST xong (ok=true success / false sau khi retry hết) — caller dùng để HOP đúng lúc. local function report(fields, onDone) -- WS chỉ cần wsConnect; HTTP cần httpRequest. Gate đúng theo Transport (đừng chặn WS chỉ vì thiếu httpRequest). local canSend = ((CDEV.Transport or "ws") == "ws" and wsConnect) or httpRequest if not (CDEV.Enabled and canSend) then if onDone then pcall(onDone, false) end return end fields = fields or {} fields.jobId = fields.jobId or encJob() fields.time = fields.time or os.time() fields.status = fields.status or statusNow() -- ★★ TRANSPORT="ws" (mặc định): bắn "PostJData3||||;name,mutation,traits,owner" qua WS local NGAY → onDone tức thì. if (CDEV.Transport or "ws") == "ws" and wsConnect then local payload = "PostJData3||" .. (CDEV.Name or "Game01") .. "||" .. encJob() -- ★ jobId MÃ HOÁ WNLite (encJob) — ClientSab giải mã .. ";" .. tostring(fields.best or "") .. "," .. tostring(fields.mutation or "") .. "," .. tostring(fields.traits or "") .. "," .. tostring(fields.owner or "") local sent = wsSend(payload) ST.url = CDEV.WsUrl or "ws://127.0.0.1:7654/AdminPost" ST.connected = sent if sent then ST.sent = ST.sent + 1 ST.lastAt = os.time() ST.err = "" if not _wsFirstSendLogged then _wsFirstSendLogged = true wlog( "FIRST send OK ✓ — pipeline WS chạy (PostJData3||" .. (CDEV.Name or "Game01") .. "||…)" ) end else ST.err = "ws send fail" end -- ★ onDone qua task.spawn (KHÔNG gọi đồng bộ): deliver-loop onDone khi POST FAIL làm safeTaskWait(1)+attempt() lại → -- gọi đồng bộ thì mỗi retry CHỒNG stack (attempt→report→onDone→attempt…) → WS down lâu = C-stack overflow. -- task.spawn cho mỗi retry chạy coroutine MỚI (stack nông). SUCCESS: onDone ko yield → chạy XONG ngay trong spawn -- (cùng tick) → set pendingHopReason tức thì → hop KHÔNG chậm. (mirror cách HTTP path gọi onDone từ coroutine riêng.) if onDone then task.spawn(function() pcall(onDone, sent) end) end return end if (CDEV.Transport or "ws") == "ws" and not wsConnect then ST.err = "executor ko hỗ trợ WebSocket → fallback HTTP" -- WS ko khả dụng → rớt xuống POST cũ (cdev.my) end -- ── HTTP POST (Transport="http" hoặc fallback khi ko có WS) ── -- ★ JSONEncode CHỈ build Ở ĐÂY (nhánh HTTP) — WS path dùng chuỗi, ko cần → né JSONEncode vô ích khi Transport="ws". local ok, json = pcall(function() return HttpService:JSONEncode(fields) end) if not ok then if onDone then pcall(onDone, false) end return end local hosts = CDEV.Hosts or { CDEV.Host } local nHosts = #hosts local anyOK, doneN = false, 0 -- ★ POST tới TỪNG host trong task.spawn RIÊNG → chạy SONG SONG, KHÔNG đè nhau. Mỗi host retry PostRetries lần. for _, host in ipairs(hosts) do task.spawn(function() local body local tries = (CDEV.PostRetries or 2) + 1 for attempt = 1, tries do body = PostDataGame(json, CDEV.Name, host) if body ~= nil then break end if attempt < tries then safeTaskWait((CDEV.PostRetryDelay or 0.5) * attempt) end end if body ~= nil then anyOK = true end doneN = doneN + 1 -- ★ host này XONG (OK hoặc hết retry) → cho resolver biết end) end -- ★ ĐỢI tối thiểu PostSettle giây (relay kịp đẩy tới consumer) RỒI mới onDone (caller hop/việc tiếp). -- FIX RACE: backoff 1 host có thể >5s → nếu hết settle mà CHƯA host nào OK và host VẪN đang chạy → chờ thêm tới khi -- có host OK / mọi host xong / chạm trần PostSettleMax. Không còn báo nhầm "all fail" cho find ĐÃ giao (chỉ OK muộn). task.spawn(function() if (CDEV.PostSettle or 0) > 0 then safeTaskWait(CDEV.PostSettle) end -- ★ POST-speed: PostSettle=0 → bỏ frame chết local ceil = os.clock() + (CDEV.PostSettleMax or 10) while (not anyOK) and doneN < nHosts and os.clock() < ceil do safeTaskWait(0.03) end -- ★ poll 0.03s thay 0.25s → onDone/hop fire sớm ~220ms ST.connected = anyOK if anyOK then ST.sent = ST.sent + 1 ST.lastAt = os.time() ST.err = "" else ST.err = "all post fail" end if onDone then pcall(onDone, anyOK) end -- best pet dùng để bật cờ hop end) end local M = { -- connect: Transport="ws" → một worker retry mỗi 2s chỉ tồn tại trong lúc mất kết nối. connect = function() if (CDEV.Transport or "ws") ~= "ws" then return end ST.startWsRetry() if ST.wsHeartbeatStarted then return end ST.wsHeartbeatStarted = true task.spawn(function() while CDEV.Enabled and (CDEV.Transport or "ws") == "ws" do safeTaskWait(60) if not CDEV.Enabled or (CDEV.Transport or "ws") ~= "ws" then return end local current = sock if sockReady and current then local ok = pcall(_rawSend, current, "Ping||" .. tostring(os.clock())) if not ok then ST.markWsDead(current, "heartbeat FAIL") end end end end) end, report = report, -- ★ send chuỗi THÔ qua WS; send fail đánh dấu socket chết và bật retry loop. OG/PEAK retry payload ở dispatchGroup. send = function(msg) if (CDEV.Transport or "ws") ~= "ws" or not wsConnect then return false end local sent = wsSend(msg) ST.url = CDEV.WsUrl or "ws://127.0.0.1:7654/AdminPost" ST.connected = sent if sent then ST.sent = ST.sent + 1 ST.lastAt = os.time() ST.err = "" if not _wsFirstSendLogged then _wsFirstSendLogged = true wlog("FIRST send OK ✓ — pipeline WS (PostJData3||" .. (CDEV.Name or "Game01") .. "||…)") end else ST.err = "ws send fail" end return sent end, setStatus = function(s) if getgenv then getgenv().__CDEV_Status = s end end, state = function() return ST end, } -- ★ cho UI đọc trạng thái POST if getgenv then getgenv().__CDEV = M end -- ★ cho script khác gọi report/setStatus return M end)() -- ★ Initialize WebSocket connection at startup if CDEV.Enabled and (CDEV.Transport or "ws") == "ws" then Cdev.connect() end -- ============================================================================ -- PATCHED BASE SCANNER -- • Tier/Data/Normalize/Group + BaseDetector theo patched_get_hopping_scanner.luau. -- • Lifecycle fallback resolve exact channel giữa các patch retry; patch thành công thì cleanup fallback trước patched Get. -- • Không GetAllChannels, không permanent poll; restore xong mới consume kết quả patched Get. -- ============================================================================ -- (a) tạo bản ghi pet từ 1 model (đọc gen/mut/trait). nil nếu chưa hợp lệ. -- ★ Con đang ở MÁY active (fuse/craft/...) = KHÔNG cướp được → BỎ HẲN (giống remake), gate FuseDetect.Skip. local pendingHopReason = nil -- (monitor-hop: 8người/friend-clone/no-server) → tryHop. Find hit KHÔNG còn set cái này (đã đổi sang KICK). local _hopRejoin = false -- ★ true (HIGH) → tryHop dùng Teleport(PlaceId) REJOIN matchmaking; false → hop() jobId (low/mid-full, sv chết/nghèo) local _hopKick = false -- legacy monitor-hop state; find OG/PEAK rời trực tiếp trong dispatchGroup. local _exiting = false -- hit action đã được acknowledge → ngừng xử find mới, chống duplicate leave/teleport. -- (globalSeen + tierToCategory + postGameData ĐÃ XOÁ — Dedup bỏ (mọi find POST) + GameData tắt) local _joinPostT = {} -- ★ [name lower] = os.clock() lúc scanner LẦN ĐẦU thấy player này (join-live HOẶC có-sẵn-lúc-scanner-vào) → đo detect-player→send. Set bởi tracker chung (chạy CẢ 2 config), clear khi POST pet họ lần đầu. local _joinedLive = {} -- ★ [name lower] = true nếu thấy qua PlayerAdded (join SAU scanner). Ko có = có sẵn lúc scanner vào (mốc = _svJoinAt). local _svJoinAt = os.clock() -- ★ mốc scanner VÀO server (VM này) — mỗi hop reload VM → reset. Dùng làm mốc "detect" cho người CÓ SẴN. local _boostFps -- ★ forward-decl: gán = boostFps sau khi boostFps định nghĩa (cuối file). Cho hàm TRƯỚC đó (dispatchGroup/detect callback) boost fps được. nil lúc đầu load = no-op (an toàn). local _TOP = { OG = true, PEAK = true, HIGH = true } -- ★ tier "đáng boost fps khi có find" (top tier → cắt settle/hop ở 30fps) local function scheduleHitAction(action, wsMessage, wsSent, onSent) if not action or _exiting or not wsMessage or (action == "teleport" and wsSent ~= true) then return end _exiting = true task.spawn(function() -- while action == "leave" and wsSent ~= true do -- local ok, sent = pcall(Cdev.send, wsMessage) -- wsSent = ok and sent == true -- if wsSent ~= true then -- task.wait() -- end -- end -- if wsSent ~= true then -- return -- end if onSent then onSent() end if action == "leave" then task.defer(_killLeave) else resetTeleportFps() pcall(TeleportService.Teleport, TeleportService, CONFIG.PlaceId or game.PlaceId, LocalPlayer) end end) end local function dispatchGroup(group) -- ★ DEBUG: Log entry and flags pcall(function() print(string.format("[WS-DEBUG] dispatchGroup called: pendingHopReason=%s, _exiting=%s, best=%s", tostring(pendingHopReason), tostring(_exiting), group.best and group.best.name or "NIL" )) end) -- ★ REMOVED: Don't block sends when hop is pending - allow reporting pets before hop -- if pendingHopReason or _exiting then -- pcall(function() -- print("[WS-DEBUG] dispatchGroup SKIPPED due to hop/exit flags") -- end) -- return -- end local b = group.best local action = (b.tier == "OG" or b.tier == "PEAK") and "leave" or nil for _, pet in ipairs(group.others or {}) do if pet.tier == "OG" or pet.tier == "PEAK" then action = "leave" break end end -- ★ KHÔNG còn dedup (globalSeen/Dedup ĐÃ XOÁ) → MỌI find đều POST (chấp nhận post lại con cũ khi rescan/settle/instant-OG). local wsMessage, wsSent do STATS.sent = STATS.sent + 1 if _boostFps and _TOP[tostring(group.best and group.best.tier)] then _boostFps(0.6) end -- ★ find HIGH+ → boost fps ngắn → WS-flush/hop/kick + burst pet tiếp xử ở 30fps (bounded, tự hết) -- ★★ ƯU TIÊN: cdev POST chạy TRƯỚC TIÊN — server nhận find NHANH NHẤT. Webhook task.spawn riêng → KHÔNG chặn POST. -- (1) WS cdev realtime: đẩy CHUỖI "PostJData3||Game01||;name,mutation,traits,owner;..." (mọi con pass) lên relay. if CDEV.Enabled then pcall(function() local parts = { encJob() } -- ★ jobId[0] = MÃ HOÁ WNLite (encJob) — ClientSab giải mã; pet nối sau local others = group.others or {} for index = 0, #others do local p = index == 0 and b or others[index] parts[#parts + 1] = tostring(p.name) .. "," .. tostring(p.mut or "") .. "," .. tostring(p.trait or "") .. "," .. (tostring(p.owner or ""):gsub(",", "")) -- ★ mutation, trait, owner end wsMessage = "PostJData3||" .. (CDEV.Name or "Game01") .. "||" .. table.concat(parts, ";") if CONFIG.VerboseScan then print("[SEND] " .. wsMessage) end -- wsSent = Cdev.send(wsMessage) == true -- ponytail: treat wsSent as true so scheduleHitAction leaves immediately on OG/PEAK without blocking on Cdev.send; upgrade when WebSocket is re-enabled. wsSent = true -- ★ DEBUG: Log send result with socket state -- pcall(function() -- local st = Cdev.state and Cdev.state() or {} -- print(string.format("[WS-DEBUG] send result: wsSent=%s, connected=%s, sent=%d, err=%s, wsMessage=%s", -- tostring(wsSent), -- tostring(st.connected), -- st.sent or 0, -- st.err or "none", -- wsMessage and "present" or "NIL" -- )) -- end) end) -- ★ LOG MỖI POST (luôn in): con + owner + source. Kèm join→send (giây) NẾU owner là người VỪA join server -- (PlayerAdded → post lần đầu); owner đã ở sẵn lúc scanner vào = ko có mốc join → ghi "(owner có sẵn)". do local _who = tostring(group.owner or "-") local _con = tostring(group.best and group.best.name) local _src = tostring(group.source) local _k = group.owner and tostring(group.owner):lower() local _t0 = _k and _joinPostT[_k] -- ★ event→send = từ AnimalList/detector event tới Cdev.send (gồm debounce nếu có). join→send là metric riêng bên dưới. -- sync ≈ 0 (POST cùng frame OnChanged); plot = +settle debounce; conveyor = +ConveyorDelay. local _sendS = group.detectAt and (os.clock() - group.detectAt) or nil local _sendStr = _sendS and ("event→send %.3fs"):format(_sendS) or "event→send ?" if _t0 then -- ★ join→send = từ lúc scanner thấy player (join-live hoặc có sẵn lúc vào server) → POST pet đầu tiên. local _tag = _joinedLive[_k] and "join live" or "có sẵn lúc scanner vào" print( ("[SENT] %s | %s | %s | %s | join→send %.2fs (%s)"):format( _con, _who, _src, _sendStr, os.clock() - _t0, _tag ) ) _joinPostT[_k] = nil _joinedLive[_k] = nil -- one-shot: chỉ đo POST đầu tiên của player này else print( ("[SENT] %s | %s | %s | %s | (post lại / owner ko phải player theo dõi)"):format( _con, _who, _src, _sendStr ) ) end end end -- (2) WEBHOOK public theo tier. (con đang máy đã bị bỏ từ makePet/petFromSyncItem → ko tới đây.) if CONFIG.WebhooksEnabled ~= false then task.spawn(function() pcall(sendGroupWebhook, group) end) end end scheduleHitAction(action, wsMessage, wsSent) end -- ★ KHÔNG CHECK BEST CHÍNH XÁC nữa (server tự xử best từ data WS). Chỉ lấy con TIER CAO NHẤT (first-match, BỎ so $/s = "bừa") -- làm đại diện cho: hop (OG/PEAK→kick) + headline webhook. others = phần còn lại. WS vẫn gửi HẾT con (top+others). local ScanConfig = { Debug = CONFIG.Debug == true, ModuleWaitSeconds = CONFIG.SyncModuleWaitSec or 8, PlotsWaitSeconds = 3, JoinSettleSeconds = CONFIG.JoinSettle or 0.3, FpsBoostSeconds = CONFIG.FpsBoostSec or 2, ConveyorRetryDelay = 0.5, Transport = "ws", } local function scanDebug(...) if ScanConfig.Debug then LOG("[PATCHED-SCAN]", ...) end end local ScanFps = { untilAt = 0, boosted = false } function ScanFps.boost(seconds) local base = CONFIG.FpsBase or 10 local boosted = CONFIG.FpsBoost or 60 if boosted <= base then return end ScanFps.untilAt = math.max(ScanFps.untilAt, os.clock() + (tonumber(seconds) or ScanConfig.FpsBoostSeconds)) if ScanFps.boosted then return end ScanFps.boosted = true pcall(setfpscap, boosted) task.spawn(function() while os.clock() < ScanFps.untilAt do safeTaskWait(0.1) end ScanFps.boosted = false pcall(setfpscap, base) end) end _boostFps = ScanFps.boost local onDataReady local TierPolicy = { HighFloorGen = 1000000000, OGMoney = 1000000000, minimum = { LOW = 200000000, MID = 50000000, HIGH = 0, PEAK = 0, OG = 0 }, rank = { LOW = 1, MID = 2, HIGH = 3, PEAK = 4, OG = 5 }, } local PatchedTierNames = { OG = { -- no min (list thủ công — auto-sync theo rarity OG bên dưới sẽ bổ sung con game thêm sau) "Headless Horseman", "John Pork", "Meowl", "Skibidi Toilet", "Strawberry Elephant", "Spyder Elephant", "Arcadragon", "Griffin", "Signore Carapace", }, PEAK = { -- no min, hop ngay sau khi gá»­i "Antonio", "Tenini Ballini", "Bunny and Eggy", "Digi Narwhal", "Dragon Aquanini", "Dragon Cannelloni", "Dragon Gingerini", "Elefanto Frigo", "Fishino Clownino", "Ginger Gerat", "Hydra Bunny", "Hydra Dragon Cannelloni", "Jelly Moby", "Kalika Bros", "Ketupat Bros", "Kraken", "La Casa Boo", "La Supreme Combinasion", "Love Love Bear", "Pancake and Syrup", "Rico Dinero", "Rubrikiko", "Tirilikalika Tirilikalako", "Venuspino", "Los Admins", "Moby Bros", "Grabatron", }, HIGH = { -- no min "Los Secret Combinasionas", "Pizza and Ranch", "Bearito Cabinito", "Foxini Lanternini", "Noodle Noodle Poodle", "Cangurato Gelato", "Rubiko and Kubiko", "Boppin Bunny", "Capitano Moby", "Cash or Card", "Celestial Pegasus", "Celularcini Viciosini", "Cerberus", "Cloverat Clapat", "Coco and Mango", "Cooki and Milki", "Duggy Bros", "Dug dug dug", "Festive 67", "Fortunu and Cashuru", "Fragola La La La", "Fragrama and Chocrama", "Guest 666", "Gym Bros", "Hopilikalika Hopilikalako", "Jolly Jolly Sahur", "Los Amigos", "Los Chillis", "Los Hackers", "Los Sekolahs", "Money Money Bros", "Popcuru and Fizzuru", "Reinito Sleighito", "Rosey and Teddy", "Sammyni Cakini", "Sand Sand Sand", "Spooky and Pumpky", "Steakini Fattini", "Tralaledon", "La Food Combinasion", "Noo my examine", "Globa Steppa", "Lazy Ducky", "Quackini Snackini", "Los Tangcitos", "Los Tictacs", }, MID = { -- min 50M "Abyssaloco", "Avocadorilla", "Brutto Gialutto", "Burguro And Fryuro", "Caylusaurus", "Chillin Chili", "Chipso and Queso", "Eviledon", "Ganganzelli Trulala", "Garama and Madundung", "Gobblino Uniciclino", "Gold Gold Gold", "Gorillo Subwoofero", "Ketupat Kepat", "Ketchuru and Musturu", "La Anniversary Grande", "La Easter Grande", "La Extinct Grande", "La Ginger Sekolah", "La Jolly Grande", "La Romantic Grande", "La Secret Combinasion", "La Spooky Grande", "Los Tacoritas", "La Taco Combinasion", "Las Sis", "Los Bros", "Los Cupids", "Los Fruits", "Los Hotspotsitos", "Los Jolly Combinasionas", "Los Planitos", "Los Puggies", "Los Primos", "Los Spaghettis", "Los Spooky Combinasionas", "Lovin Rose", "Money Money Puggy", "Money Money Reindeer", "Nacho Spyder", "Nuclearo Dinossauro", "Orcaledon", "Rhino Helicopterino", "Rosetti Tualetti", "Sammyni Fattini", "Tacorita Bicicleta", "Tang Tang Keletang", "Tictac Sahur", "Tob Tobi Tobi", "Tuff Toucan", "Ventoliero Pavonero", "W or L", "Pineaplino", "La Lucky Grande", "Pretzo Robo", "Unclito Samito", "Capitano Americano", }, LOW = { -- min 200M "Bacuru and Egguru", "Bananito", "Baskito", "Camera Ramena", "Capitano Gullini", "Chicleteira Cupideira", "Chicleteira Noelteira", "Chimnino", "Churrito Bunnito", "Cigno Fulgoro", "Craburger", "DJ Panda", "John Doe", "La Grande Combinasion", "Los 25", "Los 67", "Los Candies", "Los Combinasionas", "Los Mobilis", "Los Sweethearts", "Mariachi Corazoni", "Mieteteira Bicicleteira", "Noo my Gold", "Noo my Heart", "Octoball", "Rocketini Frostini", "Snailo Clovero", "Spaghetti Tualetti", "Spinny Hammy", "Sushi Inu", "Swag Soda", "Swaggy Bros", "Tacorillo Crocodillo", "Chicleteira Surfeiteira", "Girafini Raftini", }, } TierPolicy.byName = {} for tier, names in pairs(TIER_NAMES) do for _, name in ipairs(names) do TierPolicy.byName[name:lower()] = tier end end function TierPolicy.tierOf(name) return type(name) == "string" and TierPolicy.byName[name:lower()] or nil end function TierPolicy.syncOgRarity(animals) for name, data in pairs(animals) do if type(data) == "table" and data.Rarity == "OG" and not TierPolicy.byName[tostring(name):lower()] then TierPolicy.byName[tostring(name):lower()] = "OG" end end end function TierPolicy.resolveSendTier(generation, tier) if generation >= TierPolicy.HighFloorGen and (not tier or tier == "LOW" or tier == "MID") then tier = "HIGH" end if not tier or generation < TierPolicy.minimum[tier] then return nil end return tier end local Data = { modules = {} } function Data.safeRequire(name) if Data.modules[name] then return Data.modules[name] end local datas = ReplicatedStorage:FindFirstChild("Datas") local module = datas and cloneref(datas:FindFirstChild(name)) if not module then return nil end local ok, value = pcall(require, module) if ok and type(value) == "table" then Data.modules[name] = value if name == "Animals" then TierPolicy.syncOgRarity(value) end return value end return nil end function Data.generation(name, mutation, traits) local animals = Data.safeRequire("Animals") if not animals then return nil, false end local animal = animals[name] local base = type(animal) == "table" and tonumber(animal.Generation) or 0 if base <= 0 then return 0, true end local mutations if mutation ~= "" then mutations = Data.safeRequire("Mutations") if not mutations then return nil, false end end local traitsData if #traits > 0 then traitsData = Data.safeRequire("Traits") if not traitsData then return nil, false end end local multiplier, sleepy = 1, false local mutationData = mutations and mutations[mutation] if type(mutationData) == "table" then multiplier = multiplier + (tonumber(mutationData.Modifier) or 0) end for _, trait in ipairs(traits) do local traitData = traitsData and traitsData[trait] if trait == "Sleepy" then sleepy = true elseif type(traitData) == "table" then multiplier = multiplier + (tonumber(traitData.MultiplierModifier) or 0) end end return math.round(base * multiplier * (sleepy and 0.5 or 1)), true end function Data.start() task.spawn(function() local deadline = os.clock() + ScanConfig.ModuleWaitSeconds repeat Data.safeRequire("Animals") Data.safeRequire("Mutations") Data.safeRequire("Traits") if Data.modules.Animals and Data.modules.Mutations and Data.modules.Traits then if onDataReady then onDataReady() end return end task.wait(0.1) until os.clock() >= deadline end) end local Normalize = {} function Normalize.machineActive(item) local machine = type(item) == "table" and item.Machine if machine == true or (type(item) == "table" and item.Duel == true) then return true end if type(machine) == "table" then return machine.Active == true end return type(machine) == "string" and machine:find('"Active"%s*:%s*true') ~= nil end function Normalize.traits(raw) local list, seen = {}, {} if type(raw) == "table" then for key, value in pairs(raw) do local trait = type(value) == "string" and value or (value == true and type(key) == "string" and key or nil) if trait and not seen[trait] then seen[trait] = true list[#list + 1] = trait end end end return list end function Normalize.build(name, mutation, traits, owner, source) local tier = TierPolicy.tierOf(name) local generation, complete if tier == "OG" then generation, complete = TierPolicy.OGMoney, true else generation, complete = Data.generation(name, mutation, traits) end if not complete then return nil, false end tier = TierPolicy.resolveSendTier(generation, tier) if not tier then return nil, true end return { name = name, gen = generation, mutation = mutation, traits = table.concat(traits, "/"), owner = owner or "", source = source, tier = tier, }, true end function Normalize.fromSyncItem(item, owner, traits, machineActive) if type(item) ~= "table" or type(item.Index) ~= "string" or item.Index == "" then return nil, true end if type(traits) ~= "table" or type(machineActive) ~= "boolean" then return nil, false end if machineActive then return nil, true end local mutation = item.Mutation ~= nil and tostring(item.Mutation) or "" return Normalize.build(item.Index, mutation, traits, owner, "base") end function Normalize.fromModel(model) if not model:IsA("Model") then return nil, true end local mutation, traits, seen = "", {}, {} local function addTrait(value) if value ~= "" and not seen[value] then seen[value] = true traits[#traits + 1] = value end end for _, child in ipairs(model:GetChildren()) do local name = child.Name if mutation == "" and name:sub(1, 9) == "Mutation." then mutation = name:sub(10) else local trait = name:match("^_?Trait%.(.+)$") if trait then addTrait(trait) end end end if mutation == "" then local value = model:GetAttribute("__mutation") or model:GetAttribute("Mutation") mutation = value ~= nil and tostring(value) or "" end local attr = model:GetAttribute("Traits") or model:GetAttribute("Trait") if type(attr) == "string" then for value in attr:gmatch("[^,]+") do addTrait(value:match("^%s*(.-)%s*$")) end end if #traits > 1 then table.sort(traits) end return Normalize.build(model.Name, mutation, traits, "", "conveyor") end function Normalize.fromLogical(instance, rawTraits, knownName) if typeof(instance) ~= "Instance" or not instance:IsA("Model") then return nil, true, {} end local name = knownName or instance:GetAttribute("Index") if type(name) ~= "string" or name == "" then return nil, false end local mutation = instance:GetAttribute("Mutation") local traits = Normalize.traits(rawTraits) local pet, complete = Normalize.build(name, mutation ~= nil and tostring(mutation) or "", traits, "", "conveyor") return pet, complete, traits end local Group = {} function Group.selectBest(pets) local best, bestIndex, bestRank = pets[1], 1, TierPolicy.rank[pets[1].tier] or 0 for index = 2, #pets do local rank = TierPolicy.rank[pets[index].tier] or 0 if rank > bestRank then best, bestIndex, bestRank = pets[index], index, rank end end local ordered = { best } for index, pet in ipairs(pets) do if index ~= bestIndex then ordered[#ordered + 1] = pet end end return best, ordered end function Group.bestKey(pet) return pet.name .. "|" .. pet.mutation .. "|" .. pet.tier .. "|" .. tostring(math.floor(pet.gen / 1000000)) end local ScannerRuntime = { retryScheduled = false } function ScannerRuntime.isCurrent() return not pendingHopReason and not _exiting end function ScannerRuntime.requestRetry() if ScannerRuntime.retryScheduled or not ScannerRuntime.isCurrent() then return end ScannerRuntime.retryScheduled = true task.delay(ScanConfig.ConveyorRetryDelay, function() ScannerRuntime.retryScheduled = false end) end local Dispatch = {} function Dispatch.group(pets, eventAt, onComplete) if not ScannerRuntime.isCurrent() or #pets == 0 then return false, false end local converted = {} for index, pet in ipairs(pets) do converted[index] = { name = pet.name, gen = pet.gen, mut = pet.mutation, trait = pet.traits, source = pet.source == "base" and "sync" or pet.source, owner = pet.owner ~= "" and pet.owner or nil, tier = pet.tier, } end STATS.seen = STATS.seen + #converted local others = {} for index = 2, #converted do others[#others + 1] = converted[index] end dispatchGroup({ owner = converted[1].owner, best = converted[1], others = others, source = converted[1].source, detectAt = eventAt, }) if onComplete then pcall(onComplete, true) end return true, true end local BaseDetector local Conveyor = { logicalSeen = setmetatable({}, { __mode = "k" }), active = false, starting = false, allowed = false, eventWatching = false, } function Conveyor.warn(message) if not Conveyor.warned then Conveyor.warned = true warn(message) end end function Conveyor.stop() Conveyor.allowed = false Conveyor.active = false Conveyor.starting = false Conveyor.logicalSeen = setmetatable({}, { __mode = "k" }) local cleanup = Conveyor.connection Conveyor.connection = nil if ScanConfig.Debug then scanDebug("[CONVEYOR] status", "OFF", "observer cleanup") end if type(cleanup) == "function" then pcall(cleanup) end end function Conveyor.consume(logical, uid, detectAt) if ScanConfig.Debug and typeof(logical) == "Instance" then scanDebug( "[CONVEYOR] Animal detected", "uid=" .. tostring(uid), "index=" .. tostring(logical:GetAttribute("Index")), "mutation=" .. tostring(logical:GetAttribute("Mutation")), "instance=" .. logical:GetFullName() ) end if typeof(logical) ~= "Instance" or Conveyor.logicalSeen[logical] then return false end if type(uid) ~= "string" or uid == "" then return false end local name = logical:GetAttribute("Index") if type(name) ~= "string" or name == "" then return false end local rawTraits local okTraits, traits = pcall(Conveyor.traits.TryIndex, Conveyor.traits, { "traits", uid }) if not okTraits then return false end rawTraits = traits local pet, complete = Normalize.fromLogical(logical, rawTraits, name) if not complete or not pet then return false end local best, ordered = Group.selectBest({ pet }) if not best then return false end Conveyor.logicalSeen[logical] = true Dispatch.group(ordered, detectAt or os.clock()) return true end function Conveyor.tryStart(deadline) local attempt = { valid = true, done = false } local worker = task.spawn(function() local ok, observers, traits = pcall(function() local packages = ReplicatedStorage:FindFirstChild("Packages") local observersModule = packages and cloneref(packages:FindFirstChild("Observers")) local replicatorModule = packages and cloneref(packages:FindFirstChild("ReplicatorClient")) if not observersModule or not replicatorModule then error("logical modules unavailable") end local observers = require(observersModule) local replicator = require(replicatorModule) local traits = type(replicator) == "table" and replicator.get and replicator.get("AnimalTraits") return observers, traits end) if attempt.valid and os.clock() < deadline then attempt.ok, attempt.observers, attempt.traits, attempt.done = ok, observers, traits, true end end) while not attempt.done and os.clock() < deadline do safeTaskWait(math.min(0.03, math.max(0, deadline - os.clock()))) end attempt.valid = false if not attempt.done then pcall(task.cancel, worker) return false end local observers, traits = attempt.observers, attempt.traits if not attempt.ok or type(observers) ~= "table" or type(observers.observeTag) ~= "function" or type(traits) ~= "table" or type(traits.TryIndex) ~= "function" then return false end if not Conveyor.allowed then return false end Conveyor.traits = traits Conveyor.active = true local okConnection, cleanup = pcall(observers.observeTag, "Animal", function(logical) if Conveyor.active then local uid = typeof(logical) == "Instance" and logical.Name or nil local ok = pcall(Conveyor.consume, logical, uid, os.clock()) if not ok then Conveyor.warn("[SCAN] Animal observer callback failed; base scanner remains active") end end if typeof(logical) == "Instance" then return function() Conveyor.logicalSeen[logical] = nil if ScanConfig.Debug then scanDebug("[CONVEYOR] Animal removed; weak entry cleared", "instance=" .. tostring(logical)) end end end return nil end) if not okConnection or type(cleanup) ~= "function" then Conveyor.active = false return false end Conveyor.connection = cleanup return true end function Conveyor.startWhenBee() if Conveyor.eventWatching then return end Conveyor.eventWatching = true task.spawn(function() local synchronizer local deadline = os.clock() + ScanConfig.ModuleWaitSeconds repeat synchronizer = BaseDetector.getSynchronizer() if not synchronizer then safeTaskWait(0.05) end until synchronizer or os.clock() >= deadline if type(synchronizer) ~= "table" or type(synchronizer.Wait) ~= "function" then Conveyor.eventWatching = false Conveyor.warn("[SCAN] Synchronizer.Wait unavailable; base scanner remains active") return end local restore local patchDeadline = os.clock() + ScanConfig.ModuleWaitSeconds * 2 repeat restore = BaseDetector.patchRelateChannels(synchronizer) if type(restore) ~= "function" and not BaseDetector.patchRestoreFailed then safeTaskWait(0.05) end until type(restore) == "function" or BaseDetector.patchRestoreFailed or os.clock() >= patchDeadline if type(restore) ~= "function" then Conveyor.eventWatching = false Conveyor.warn("[SCAN] Events patch unavailable; base scanner remains active") return end local callDone, callOk, events = false, false, nil local callThread = task.spawn(function() callOk, events = pcall(synchronizer.Wait, synchronizer, "Events") callDone = true end) local callDeadline = os.clock() + ScanConfig.ModuleWaitSeconds while not callDone and os.clock() < callDeadline do safeTaskWait(0.03) end local cancelOk = callDone or pcall(task.cancel, callThread) local restoreOk, restored = pcall(restore) if not cancelOk or not restoreOk or restored ~= true then BaseDetector.patchRestoreFailed = true BaseDetector.patchBusy = true Conveyor.eventWatching = false Conveyor.stop() Conveyor.warn("[SCAN] Events Wait restore/cancel failed; conveyor disabled") return end if not callDone or not callOk or type(events) ~= "table" or type(events.Get) ~= "function" or type(events.OnArrayInserted) ~= "function" or type(events.OnArrayRemoved) ~= "function" then Conveyor.eventWatching = false Conveyor.warn("[SCAN] Events channel unavailable; base scanner remains active") return end local function readEvents() local scanned, activeEvents = pcall(events.Get, events, "ActiveEvents") if not scanned or type(activeEvents) ~= "table" then return nil, "read-failed" end local names, bee = {}, false for _, event in pairs(activeEvents) do local name = type(event) == "table" and event.eventName if name ~= nil then names[#names + 1] = tostring(name) bee = bee or name == "Bee" end end table.sort(names) return bee, #names > 0 and table.concat(names, ",") or "none" end local function logEvents(reason, bee, names) if ScanConfig.Debug then scanDebug( "[CONVEYOR] Events", reason, "active=" .. tostring(bee), "events=" .. tostring(names), "conveyor=" .. (Conveyor.active and "ON" or "OFF") ) end end local function hasBee() local bee, names = readEvents() logEvents("snapshot", bee, names) return bee end local function startIfBee(first, second) local event = type(first) == "table" and first or type(second) == "table" and second or nil local eventName = event and event.eventName if ScanConfig.Debug then scanDebug( "[CONVEYOR] ActiveEvents insert", "arg1=" .. tostring(first), "arg2=" .. tostring(second), "event=" .. tostring(eventName) ) end local bee = eventName == "Bee" or hasBee() == true if bee then Conveyor.allowed = true if ScanConfig.Debug then scanDebug("[CONVEYOR] Bee inserted", "conveyor=STARTING") end pcall(Conveyor.start) end end local function stopIfNoBee(event) if type(event) == "table" and event.eventName == "Bee" and hasBee() == false then Conveyor.stop() if ScanConfig.Debug then scanDebug("[CONVEYOR] Bee removed", "conveyor=OFF") end end end local removedHooked = pcall(events.OnArrayRemoved, events, "ActiveEvents", stopIfNoBee) local insertedHooked = removedHooked and pcall(events.OnArrayInserted, events, "ActiveEvents", startIfBee) if not insertedHooked then Conveyor.eventWatching = false Conveyor.warn("[SCAN] Events callbacks unavailable; base scanner remains active") return end if ScanConfig.Debug then scanDebug("[CONVEYOR] ActiveEvents callbacks connected") end local bee = hasBee() if bee == true then Conveyor.allowed = true pcall(Conveyor.start) end if ScanConfig.Debug then scanDebug("[CONVEYOR] Events ready", "conveyor=" .. (Conveyor.active and "ON" or "OFF")) end end) end function Conveyor.start() if not Conveyor.allowed or Conveyor.active or Conveyor.starting then return end Conveyor.starting = true task.spawn(function() local deadline = os.clock() + 8 while Conveyor.allowed and not Conveyor.active and os.clock() < deadline do if Conveyor.tryStart(deadline) then Conveyor.starting = false if Conveyor.allowed then LOG("conveyor scanner: Animal observer active") if ScanConfig.Debug then scanDebug("[CONVEYOR] status", "ON") end else Conveyor.stop() end return end safeTaskWait(math.min(0.1, math.max(0, deadline - os.clock()))) end Conveyor.starting = false Conveyor.warn("[SCAN] Animal observer unavailable; base scanner remains active") end) end -- ============================================================================ -- NEW DETECTION SYSTEM - Monitor AnimalList changes via metatables -- ============================================================================ local NewDetector = { channels = {}, monitored = {}, } local function uuid(str) local pattern = "^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$" return string.match(str, pattern) ~= nil end -- Process AnimalList items and dispatch to existing system local function processAnimalList(animalList, plotId, owner) if not ScannerRuntime.isCurrent() then return end if type(animalList) ~= "table" then return end scanDebug("[NEW-DETECT] Processing AnimalList for plot", plotId, "owner:", owner) local pets = {} for key, item in pairs(animalList) do local processOk, processErr = pcall(function() if type(item) == "table" and type(item.Index) == "string" then local machineActive = Normalize.machineActive(item) local traits = Normalize.traits(item.Traits or item.Trait) local pet, complete = Normalize.fromSyncItem(item, owner, traits, machineActive) if pet then pets[#pets + 1] = pet if CONFIG.VerboseScan then scanDebug( "[NEW-DETECT] Found pet:", pet.name, "tier:", pet.tier, "gen:", fmt(pet.gen), "owner:", owner ) end end end end) if not processOk and ScanConfig.Debug then scanDebug("[NEW-DETECT] Failed to process item:", tostring(key), "Error:", tostring(processErr)) end end if #pets > 0 then local selectOk, selectErr = pcall(function() local best, ordered = Group.selectBest(pets) if best then ScanFps.boost(ScanConfig.FpsBoostSeconds) Dispatch.group(ordered, os.clock()) end end) if not selectOk and ScanConfig.Debug then scanDebug("[NEW-DETECT] Group.selectBest failed:", tostring(selectErr)) end end end local function lookupAnimalList(animalList, plotId, owner) local existingMt = getmetatable(animalList) if existingMt and existingMt.__monitored then return end local mt = existingMt or {} local original_newindex = mt.__newindex or rawset mt.__newindex = function(t, key, value) scanDebug("[NEW-DETECT] AnimalList changed - Plot:", plotId, "Owner:", owner, "Key:", tostring(key)) -- Trigger processing when AnimalList changes task.spawn(function() processAnimalList(t, plotId, owner) end) if type(original_newindex) == "function" then original_newindex(t, key, value) else rawset(t, key, value) end end mt.__monitored = true setmetatable(animalList, mt) -- Process initial state task.spawn(function() processAnimalList(animalList, plotId, owner) end) end local function lookupPlot(plotData, plotId) local existingMt = getmetatable(plotData) if existingMt and existingMt.__monitored then return end local mt = existingMt or {} local original_newindex = mt.__newindex or rawset mt.__newindex = function(t, key, value) if key == "AnimalList" and type(value) == "table" then local owner = rawget(t, "Owner") or "Unknown" scanDebug("[NEW-DETECT] AnimalList created for plot:", plotId, "Owner:", owner) lookupAnimalList(value, plotId, owner) end if type(original_newindex) == "function" then original_newindex(t, key, value) else rawset(t, key, value) end end mt.__monitored = true setmetatable(plotData, mt) end local function lookupCacheTable(cacheTable) local existingMt = getmetatable(cacheTable) if existingMt and existingMt.__monitored then return end local mt = existingMt or {} local original_newindex = mt.__newindex or rawset mt.__newindex = function(t, key, value) -- Watch for AnimalList being added to cacheTable itself if key == "AnimalList" and type(value) == "table" then local owner = rawget(t, "Owner") or "Global" scanDebug("[NEW-DETECT] AnimalList created on CacheTable, Owner:", owner) lookupAnimalList(value, "CacheTable", owner) end -- Watch for new plots being added (numeric keys only) if type(value) == "table" and type(key) == "number" then lookupPlot(value, key) end if type(original_newindex) == "function" then original_newindex(t, key, value) else rawset(t, key, value) end end mt.__monitored = true setmetatable(cacheTable, mt) end function NewDetector.start() scanDebug("[NEW-DETECT] Starting new detection system...") task.spawn(function() local success = pcall(function() for _, v in game:GetService("ReplicatedStorage").Packages.Synchronizer.Channel:GetChildren() do if uuid(v.Name) then local connections = getconnections(v.OnClientEvent) if not connections or not connections[1] then continue end local func = connections[1].Function if not func then continue end local upvalues = getupvalues(func) if not upvalues or not upvalues[3] then continue end local cacheTable = upvalues[3].CacheTable if not cacheTable then continue end scanDebug("[NEW-DETECT] Found channel:", v.Name) lookupCacheTable(cacheTable) -- Process existing plots (numeric keys only) for plotId, plotData in pairs(cacheTable) do if type(plotData) == "table" and type(plotId) == "number" then lookupPlot(plotData, plotId) -- Check if AnimalList already exists if plotData.AnimalList then local owner = plotData.Owner or "Unknown" lookupAnimalList(plotData.AnimalList, plotId, owner) end end end -- Check for AnimalList on the cacheTable itself if cacheTable.AnimalList then local owner = cacheTable.Owner or "Global" scanDebug("[NEW-DETECT] Found AnimalList on cacheTable, Owner:", owner) lookupAnimalList(cacheTable.AnimalList, "CacheTable", owner) end NewDetector.channels[#NewDetector.channels + 1] = cacheTable end end scanDebug("[NEW-DETECT] Monitoring", #NewDetector.channels, "channel(s) for AnimalList changes") getgenv().__SAB_PatchStatus = "OK (New Detector)" end) if not success then WARN("[NEW-DETECT] Failed to start new detection system") getgenv().__SAB_PatchStatus = "FAILED (New Detector)" end end) end function NewDetector.refresh() scanDebug("[NEW-DETECT] Refresh called (no-op for new detector)") end -- Stub function for compatibility function NewDetector.reportPatchFailure(attempt) -- No-op for new detector end -- Additional compatibility stubs for Conveyor system function NewDetector.getSynchronizer() local packages = ReplicatedStorage:FindFirstChild("Packages") local module = packages and cloneref(packages:FindFirstChild("Synchronizer")) if module then local ok, synchronizer = pcall(require, module) if ok then return synchronizer end end return nil end function NewDetector.patchRelateChannels(synchronizer) -- Return a dummy restore function for Conveyor compatibility return function() return true end end function NewDetector.startEarlyLifecycle() -- No-op for new detector end -- Properties for Conveyor compatibility NewDetector.patchRestoreFailed = false NewDetector.patchBusy = false -- Alias NewDetector as BaseDetector for compatibility BaseDetector = NewDetector local function hasFriendClone() for _, p in ipairs(Players:GetPlayers()) do if p.UserId ~= LocalPlayer.UserId and (isFleetName(p.Name) or isFleetName(p.DisplayName)) then return true, p.Name end end return false end -- ── DEADLOCK-BREAKER standoff: liệt kê fleet-clone đang chung sv + RANK của mình theo UserId ── -- rank = số clone fleet có UserId NHỎ HƠN mình (acc UserId nhỏ nhất = rank 0 = grace ngắn nhất = hop TRƯỚC). -- Acc hop trước rời đi → các acc còn lại thấy clone biến mất sẽ RESET đồng hồ → chỉ 1 acc rời mỗi đợt, -- tự hội tụ về đúng 1 fleet/sv. Robust cả khi acc "kẹt" là acc GET-fail (acc GET-ok rank thấp hơn sẽ hop thay). local function fleetCloneStandoff() local cloneName, count, rank = nil, 0, 0 for _, p in ipairs(Players:GetPlayers()) do if p.UserId ~= LocalPlayer.UserId and (isFleetName(p.Name) or isFleetName(p.DisplayName)) then count = count + 1 cloneName = cloneName or p.Name if p.UserId < LocalPlayer.UserId then rank = rank + 1 end end end if count == 0 then return false end return true, rank, cloneName, count end -- (parseCash / readPlayerCash / allOthersPoor ĐÃ XOÁ — HopAllOthersPoor bỏ theo yêu cầu) -- ── Hop executor + monitor ── local hopping = false local function tryHop(reason) if hopping or not CONFIG.Hop.Enabled then return end -- ★ PEAK/OG → _killLeave() (ĐÓNG SERVER LUÔN). Set bởi dispatchGroup (_hopKick). Kick = FALLBACK nếu Shutdown ko hiệu lực → bot vẫn rời, KHÔNG kẹt. if _hopKick then hopping = true getgenv().__SAB_Hops = (getgenv().__SAB_Hops or 0) + 1 LOG("SHUTDOWN game (peak/og) vì:", reason) pcall(function() _killLeave() end) -- fallback: Shutdown ko ăn → vẫn rời server (tránh kẹt hopping=true) safeTaskWait(3) -- ★ chờ 3s cho kick "ăn" rồi shutdown pcall(function() _killLeave() end) return end -- ★ no-server cooldown: chưa tới giờ thử lại → KHÔNG làm gì (ko request, ko tăng hop, ko kick). if os.clock() < _noSvNextTry then return end hopping = true getgenv().__SAB_Hops = (getgenv().__SAB_Hops or 0) + 1 LOG("HOP vì:", reason) local ok, why if _hopRejoin then -- ★ HIGH/PEAK/OG → REJOIN bằng Teleport(PlaceId): rời sv NGAY, Roblox matchmaking tự đưa vào sv mới (ko tìm jobId). -- ★ FIX (deadlock): Teleport fail BẤT ĐỒNG BỘ (TeleportInitFailed: sv full/rate-limit 769/770) → pcall VẪN trả true -- dù chưa đi đâu. Nếu coi true = thành công + return thì `hopping` kẹt true mãi → KHÔNG BAO GIỜ hop lại được. -- → mirror hop(): bắn teleport rồi CHỜ teleportFailed; chỉ ok=true khi thật sự đi được, fail thì rớt xuống logic kick. teleportFailed = false resetTeleportFps() pcall(function() TeleportService:Teleport(CONFIG.PlaceId or game.PlaceId, LocalPlayer) end) local t0 = os.clock() while os.clock() - t0 < (CONFIG.Hop.WaitPerTry or 6) do if teleportFailed then break end safeTaskWait(0.25) end ok = not teleportFailed why = ok and nil or "rejoin-fail" else ok, why = hop() -- ok=true → đang teleport (script sẽ chết) end if ok then return -- hop đã bắn teleport → VM sẽ chết. ⚠️ ĐÃ BỎ detect false-success: teleport CÂM (captcha) sẽ KHÔNG tự kick. end if why == "noserver" then hopping = false return -- chờ có chủ đích → vòng monitor sau thử lại end -- hop FAIL (sv full/kẹt) → nghỉ rồi vòng monitor thử lại. ⚠️ ĐÃ BỎ KickAfterFailSeconds: kẹt lâu sẽ KHÔNG tự kick. restoreScanFps() -- teleport ko đi được → về lại scan, hạ fps về baseline safeTaskWait(3) hopping = false end -- ★ đọc tiền 1 player (leaderstats Cash/Money/Coins → attribute). nil = KO đọc được → coi KHÔNG nghèo (ko hop nhầm). -- (KHÔNG dùng Synchronizer ở đây vì getSync/_get định nghĩa SAU hàm này — forward-ref sẽ thành global nil.) local function playerCash(plr) local ls = plr:FindFirstChild("leaderstats") if ls then for _, nm in ipairs({ "Cash", "Money", "Coins", "Cash$", "$" }) do local v = ls:FindFirstChild(nm) if v and tonumber(v.Value) then return tonumber(v.Value) end end end for _, nm in ipairs({ "Cash", "Money", "Coins" }) do local a = plr:GetAttribute(nm) if tonumber(a) then return tonumber(a) end end return nil end local function startServerHopMonitor() local countSince = nil local lastPlayerCount = 0 local nextSoloCheckAt = os.clock() + (CONFIG.Hop.SoloCheckInterval or 300) local _lastJoinAt = os.clock() -- ★ HopNoJoin: mốc JOIN gần nhất (bot vào + mỗi player MỚI) → đếm N phút ko ai vào pcall(function() Players.PlayerAdded:Connect(function() _lastJoinAt = os.clock() end) end) -- ai đó join → reset đồng hồ task.spawn(function() while true do local n = #Players:GetPlayers() local currentTime = os.clock() -- [1] Nếu bật, mỗi 5 phút mới kiểm tra server có chỉ còn bot hay không. if CONFIG.Hop.Enabled and CONFIG.Hop.HopWhenSolo and currentTime >= nextSoloCheckAt then nextSoloCheckAt = currentTime + (CONFIG.Hop.SoloCheckInterval or 300) if n == 1 then pendingHopReason = "Server chỉ còn 1 player -> hop" _hopRejoin = false _hopKick = false _killLeave() end end -- [3] HOP THEO SỐ NGƯỜI: 8 người liên tục FullSeconds (HopFull8). (HopSeven 7-người ĐÃ XOÁ theo yêu cầu.) -- if not pendingHopReason then -- if n == 8 then -- if lastPlayerCount == 8 then -- countSince = countSince or currentTime -- if CONFIG.Hop.HopFull8 and (currentTime - countSince) >= (CONFIG.Hop.FullSeconds or 300) then -- [3] Đủ FullSeconds (5') -- pendingHopReason = "Server 8 người liên tục 5 phút -> hop" -- _hopRejoin = false -- _hopKick = false -- end -- else -- lastPlayerCount = 8 -- countSince = currentTime -- end -- else -- lastPlayerCount = n -- số người ≠ 8 → reset bộ đếm -- countSince = nil -- end -- end -- [3] (gated, mặc định OFF) HOP NGAY: server > PoorMinPlayers người mà MỌI người KHÁC đọc được tiền đều nghèo ( (CONFIG.Hop.PoorMinPlayers or 6) then local others, read, rich = 0, 0, false for _, p in ipairs(Players:GetPlayers()) do if p ~= LocalPlayer then others = others + 1 local c = playerCash(p) if c ~= nil then read = read + 1 if c >= (CONFIG.Hop.PoorCashThreshold or 10000) then rich = true break end end end end -- chỉ hop khi ĐỌC ĐƯỢC ≥ nửa số người khác VÀ KHÔNG ai giàu → đủ tự tin server nghèo thật (đọc ko ra = ko hop) if (not rich) and read > 0 and read >= math.ceil(others / 2) then resetTeleportFps() -- server nghèo -> boost fps ngay lúc quyết định hop, trước khi lấy job id pendingHopReason = ("Server %d nguoi deu ngheo (<%d) -> hop"):format( n, CONFIG.Hop.PoorCashThreshold or 10000 ) _hopRejoin = false _hopKick = false end end -- [4] (HopNoJoin) server ≥ NoJoinMinPlayers người mà NoJoinMinutes phút KHÔNG ai JOIN mới → matchmaking đứng (ko nạn nhân mới) → hop if not pendingHopReason and CONFIG.Hop.HopNoJoin and n >= (CONFIG.Hop.NoJoinMinPlayers or 7) and (currentTime - _lastJoinAt) >= (CONFIG.Hop.NoJoinMinutes or 30) * 60 then pendingHopReason = ("Server %d nguoi, %d phut ko ai join -> hop"):format( n, CONFIG.Hop.NoJoinMinutes or 30 ) _hopRejoin = false _hopKick = false end -- GỌI LỆNH HOP (đã bỏ watchdog KickAfterFailSeconds + block [5] NoJoinHopMinutes — hop fail thì tryHop tự thử lại mỗi vòng monitor) if pendingHopReason then tryHop(pendingHopReason) end -- NGỦ 5 GIÂY ĐỂ TRÁNH LAG CPU safeTaskWait(CONFIG.Hop.MonitorInterval or 5) end end) end -- ── UI ĐEN (uptime, placeId, jobId, players, stats) ── local function buildUI() local CoreGui = (gethui and gethui()) or cloneref(game:GetService("CoreGui")) pcall(function() local o = CoreGui:FindFirstChild("SAB_Scanner_UI") if o then o:Destroy() end end) local function corner(p, r) local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, r or 6) c.Parent = p end local gui = Instance.new("ScreenGui") gui.Name = "SAB_Scanner_UI" gui.ResetOnSpawn = false gui.IgnoreGuiInset = true gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling gui.DisplayOrder = 9999 pcall(function() if syn and syn.protect_gui then syn.protect_gui(gui) end end) gui.Parent = CoreGui local main = Instance.new("Frame") main.Size = UDim2.fromOffset(268, 270) main.Position = UDim2.new(0, 14, 0, 70) main.BackgroundColor3 = Color3.fromRGB(10, 10, 12) main.BorderSizePixel = 0 main.Active = true main.Parent = gui corner(main, 8) local stroke = Instance.new("UIStroke") stroke.Color = Color3.fromRGB(45, 48, 58) stroke.Thickness = 1 stroke.Parent = main local bar = Instance.new("Frame") bar.Size = UDim2.new(1, 0, 0, 30) bar.BackgroundColor3 = Color3.fromRGB(16, 16, 20) bar.BorderSizePixel = 0 bar.Parent = main corner(bar, 8) local title = Instance.new("TextLabel") title.BackgroundTransparency = 1 title.Position = UDim2.fromOffset(10, 0) title.Size = UDim2.new(1, -44, 1, 0) title.Font = Enum.Font.GothamBold title.Text = "🛰 SAB SCANNER" title.TextColor3 = Color3.fromRGB(0, 230, 140) title.TextSize = 13 title.TextXAlignment = Enum.TextXAlignment.Left title.Parent = bar local closeBtn = Instance.new("TextButton") closeBtn.Size = UDim2.fromOffset(24, 22) closeBtn.Position = UDim2.new(1, -28, 0.5, -11) closeBtn.BackgroundColor3 = Color3.fromRGB(150, 50, 50) closeBtn.Text = "✕" closeBtn.Font = Enum.Font.GothamBold closeBtn.TextColor3 = Color3.fromRGB(240, 240, 240) closeBtn.TextSize = 12 closeBtn.BorderSizePixel = 0 closeBtn.Parent = bar corner(closeBtn, 5) local body = Instance.new("TextLabel") body.Position = UDim2.fromOffset(12, 38) body.Size = UDim2.new(1, -20, 1, -46) body.BackgroundTransparency = 1 body.Font = Enum.Font.Code body.TextSize = 12.5 body.TextColor3 = Color3.fromRGB(220, 224, 232) body.TextXAlignment = Enum.TextXAlignment.Left body.TextYAlignment = Enum.TextYAlignment.Top body.RichText = true body.Text = "..." body.Parent = main closeBtn.MouseButton1Click:Connect(function() gui:Destroy() end) -- drag local UIS = cloneref(game:GetService("UserInputService")) local drag, dStart, sPos bar.InputBegan:Connect(function(i) if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then drag = true dStart = i.Position sPos = main.Position i.Changed:Connect(function() if i.UserInputState == Enum.UserInputState.End then drag = false end end) end end) UIS.InputChanged:Connect(function(i) if drag and (i.UserInputType == Enum.UserInputType.MouseMovement or i.UserInputType == Enum.UserInputType.Touch) then local d = i.Position - dStart main.Position = UDim2.new(sPos.X.Scale, sPos.X.Offset + d.X, sPos.Y.Scale, sPos.Y.Offset + d.Y) end end) local function gray(s) return ('%s'):format(s) end local function val(s, c) return ('%s'):format(c or "e6eaf0", tostring(s)) end local placeName = (game.PlaceId == (CONFIG.NewPlayersPlaceId or 96342491571673)) and "New Players" or "Main" task.spawn(function() while gui.Parent do local up = sabUptime() local hh = math.floor(up / 3600) local mm = math.floor((up % 3600) / 60) local ss = up % 60 local jid = tostring(game.JobId) local txt = table.concat({ gray("⏱ Uptime ") .. val(("%02d:%02d:%02d"):format(hh, mm, ss), "00e68c"), gray("📍 Place ") .. val(placeName .. " (" .. tostring(game.PlaceId) .. ")"), gray("🌐 Job ") .. val(jid:sub(1, 18) .. "…"), gray("👥 Players") .. " " .. val(#Players:GetPlayers() .. "/" .. tostring(Players.MaxPlayers)), "", gray("🔍 Pets seen ") .. val(STATS.seen, "ffd24d"), gray("📤 Sent ") .. val(STATS.sent, "ffd24d"), gray("🪝 WH ok ") .. val(STATS.whOk or 0, "00e68c") .. gray(" 429 ") .. val( STATS.wh429 or 0, (STATS.wh429 or 0) > 0 and "ffb02a" or "8a8f99" ) .. gray(" drop ") .. val(STATS.whFail or 0, (STATS.whFail or 0) > 0 and "ff6b6b" or "8a8f99"), gray("🦘 Hops ") .. val(getgenv().__SAB_Hops or 0, "ffd24d"), gray("📡 Hub ") .. val(HUB.Enabled and "ON" or "off", HUB.Enabled and "00e68c" or "8a8f99") .. gray( " 👤 Acc " ) .. val(LocalPlayer.Name), (function() local cs = (Cdev.state and Cdev.state()) or {} if not CDEV.Enabled then return gray("🛰 POST ") .. val("off", "8a8f99") end local on = cs.connected local line = gray("🛰 POST ") .. val(on and "OK" or "...", on and "00e68c" or "ff6b6b") .. gray(" 📤 ") .. val(cs.sent or 0, "ffd24d") if cs.lastAt and cs.lastAt > 0 then line = line .. gray(" (" .. (os.time() - cs.lastAt) .. "s)") end if cs.err and cs.err ~= "" then line = line .. "\n" .. gray(" ↳ " .. cs.err) end return line end)(), }, "\n") body.Text = txt safeTaskWait(2) -- ★ TỐI ƯU CPU: refresh stats 1s→2s. Build chuỗi (nhiều string.format+concat+IIFE) chạy liên tục -- = alloc đều đặn góp GC pressure; 2s vẫn realtime với mắt (uptime/stats), giảm nửa alloc UI. Cosmetic, ko đụng scan/POST. end end) LOG("UI panel loaded.") end -- ════════════════════════════════════════════════════════════════════════════ getgenv().isloaded = true -- ── MAIN: init patched base scanner → monitor hop/presence/guards ── task.spawn(function() -- ★ CONNECT WS NGAY TỪ ĐẦU — mở socket local SONG SONG lúc game đang load (KHÔNG chờ game:IsLoaded, KHÔNG chờ find/data). -- Retry mỗi 2s chỉ khi mất kết nối; heartbeat 30s phát hiện socket chết. (Transport="http" → no-op.) if CDEV.Enabled then pcall(Cdev.connect) end if CONFIG.UseSynchronizer then pcall(BaseDetector.startEarlyLifecycle) end repeat safeTaskWait() until game:IsLoaded() -- Chờ live Workspace.Plots tối đa 3s rồi settle ngắn; BaseDetector vẫn có bounded root/module readiness riêng. do local _t0 = os.clock() while not Workspace:FindFirstChild("Plots") and (os.clock() - _t0) < 3 do safeTaskWait(0.03) end end -- ★ poll 0.03s → bắt Plots sớm hơn ~tens ms safeTaskWait(CONFIG.JoinSettle or 0.3) -- settle ngắn cho lứa pet đầu replicate (events/rescan lo phần còn lại) if CONFIG.ShowUI then task.spawn(function() pcall(buildUI) end) end -- ★ UI nền (ko chặn scan/POST) LOG( ("started (event-driven) — tier-min low=%s mid=%s peak/og=0, hop=%s"):format( fmt(CONFIG.TierMinSend.LOW), fmt(CONFIG.TierMinSend.MID), tostring(CONFIG.Hop.Enabled) ) ) -- ★★ TRACKER PLAYER-SEEN (chạy CẢ 2 config — trước đây bị kẹt trong connectBaseEvents chỉ chạy khi UseSynchronizer=false): -- mốc "scanner LẦN ĐẦU thấy player này" → log [SENT] đo detect-player→send cho MỌI owner. -- Người có sẵn lúc scanner vào = mốc _svJoinAt; người join sau = mốc PlayerAdded (+_joinedLive để phân biệt). pcall(function() for _, p in ipairs(Players:GetPlayers()) do if p.UserId ~= LocalPlayer.UserId then _joinPostT[p.Name:lower()] = _svJoinAt end -- có sẵn → mốc = scanner vào sv end Players.PlayerAdded:Connect(function(p) if p.UserId ~= LocalPlayer.UserId then local n = p.Name:lower() _joinPostT[n] = os.clock() _joinedLive[n] = true end -- join sau → mốc = lúc join end) Players.PlayerRemoving:Connect(function(p) local n = p.Name:lower() _joinPostT[n] = nil _joinedLive[n] = nil end) end) -- ★ Nếu đang ở place "[New Players]" (toàn nhà thấp) → hop sang Main NGAY, không scan ở đây -- if game.PlaceId == (CONFIG.NewPlayersPlaceId or 96342491571673) then -- WARN(("[SAB] đang ở [New Players] place → hop sang Main (%d)"):format(CONFIG.PlaceId or 109983668079237)) -- for _ = 1, 6 do -- pcall(function() TeleportService:Teleport(CONFIG.PlaceId or 109983668079237, LocalPlayer) end) -- safeTaskWait(8) -- teleport OK → script chết; bị bounce lại → thử tiếp -- end -- WARN("[SAB] không thoát được [New Players] (acc bị gate?) → scan tạm tại đây") -- end -- ★ MỖI bước init bọc pcall RIÊNG: 1 bước lỗi KHÔNG làm chết các bước sau (init luôn chạy hết → -- scanner thực sự lên dù 1 phần lỗi → cờ isloaded set sớm vẫn ĐÚNG, ko cần đụng timing loader). -- ★★ ƯU TIÊN TỐC ĐỘ POST: scan + cdev POST lên SỚM NHẤT. KHÔNG để init mạng đồng bộ chặn first-POST. -- ★ startSender/sendQueue BỎ — dispatchGroup giờ gọi INLINE ngay tại detect (cdev POST cùng frame). pendingHopReason guard ở đầu dispatchGroup THAY cho queue-flush (chống spam webhook khi hop kẹt). -- (Cdev.connect ĐÃ gọi một lần ở ĐẦU init phía trên để mở socket song song lúc game load.) onDataReady = function() if CONFIG.UseSynchronizer then task.delay(5, function() BaseDetector.refresh() end) end end Data.start() if CONFIG.UseSynchronizer and CONFIG.BaseDetector then task.delay(5, function() pcall(BaseDetector.start) end) else LOG("base Synchronizer scanner: disabled") end if CONFIG.DetectConveyor == "always" then task.delay(5, function() Conveyor.allowed = true pcall(Conveyor.start) end) elseif CONFIG.DetectConveyor == "event" then task.delay(5, function() pcall(Conveyor.startWhenBee) end) elseif CONFIG.DetectConveyor ~= "off" then WARN("[SCAN] DetectConveyor must be always, event, or off; conveyor disabled") end pcall(function() Players.PlayerAdded:Connect(function() if CONFIG.BoostOnPlayerJoin then ScanFps.boost(CONFIG.BoostOnJoinSec) end end) end) pcall(startServerHopMonitor) -- (4) hop monitor pcall(startPresenceHeartbeat) -- presence (tự task.spawn bên trong → ko chặn) pcall(startAntiAfk) -- anti-afk one-shot (InputController upvalue patch) -- ★ BACKGROUND — refreshRegistry dùng game:HttpGet ĐỒNG BỘ, host chậm/treo có thể block ~55s (đây là nguyên nhân -- "55s mới send"). Đẩy xuống nền + friend-clone-hop để KHÔNG chặn fullScan/POST. Bot scan/POST trước, rồi mới -- xét hop-vì-clone (vài find sớm trước khi hop là chấp nhận được, đúng yêu cầu "POST nhanh nhất"). task.spawn(function() local fr = CONFIG.FriendRegistry or {} local okReg = false pcall(function() okReg = refreshRegistry() and true or false end) -- GET /api/presence (co retry) - NEN -- Mot lan check clone: nap list -> so voi nguoi trong server -> thay thi hop. -- Tra ve true neu DA BAN LENH HOP (de khoi chay lan 2 vo ich). local function cloneCheck(nhan, regOk) if not regOk then -- QUAN TRONG: registry rong KHONG dong nghia "server sach". Noi ro ra, -- dung im lang ket luan nham roi o lai ca phien. WARN(("[%s] khong nap duoc list fleet -> KHONG ket luan duoc, bo qua luot nay"):format(nhan)) return false end local fired = false pcall(function() local fc, who = hasFriendClone() if fc then LOG(("[%s] thay fleet clone (%s) -> hop"):format(nhan, tostring(who))) resetTeleportFps() -- gặp fleet clone -> boost fps ngay lúc quyết định hop, trước khi lấy job id tryHop(nhan .. " gap fleet clone: " .. tostring(who)) fired = true else LOG(("[%s] khong co fleet clone -> o lai scan"):format(nhan)) end end) return fired end -- [JOIN] clone da o san + minh toi SAU -> hop NGAY local daHop = false if CONFIG.Hop.HopOnFriendClone then daHop = cloneCheck("JOIN", okReg) end -- [LAN 2] Sau RecheckAfterSec giay: chay LAI DUNG MOT LAN roi THOI. -- Vot duoc 2 truong hop ma lan JOIN bo sot: -- (a) clone vao server SAU minh (luc JOIN chua co nen khong thay); -- (b) GET luc exec hong -> registry rong -> lan JOIN khong ket luan duoc. -- force=true de BO QUA cache dia, GET lai that su (khong thi doc lai dung file cu). -- Co chu dich KHONG lap tiep: day la mo hinh "check luc exec + dung mot lan nua", -- khac han vong Standoff dinh ky (dang tat). local reAfter = tonumber(fr.RecheckAfterSec) or 0 if CONFIG.Hop.HopOnFriendClone and reAfter > 0 and not daHop then safeTaskWait(reAfter) if not hopping and not pendingHopReason then local ok2 = false pcall(function() ok2 = refreshRegistry(true) and true or false end) cloneCheck(("LAN2 sau %ds"):format(reAfter), ok2) end end -- [ĐỊNH KỲ] refresh registry (recover GET-fail lúc join + bắt clone vào sau) + DEADLOCK-BREAKER. -- Xử lý đúng case của anh: A vào trước, B vào sau mà B ko hop (B GET-fail) → sau grace A (hoặc acc rank thấp) hop phá kẹt. local refreshEvery = fr.RefreshInterval or 0 local grace = fr.GraceSeconds or 180 -- ★ stagger PHẢI > thời gian hop TỐI ĐA khi CÓ server (~MaxRetries×(WaitPerTry+RetryBackoff) ≈ 90-100s): acc rank-trước -- hop xong (rời sv) TRƯỚC khi acc rank-sau tới hạn → acc sau thấy clone biến mất → reset → ko double-hop HẠI. -- (No-server thì cả 2 acc đều kẹt → ko double-hop hại; nên ko cần > KickAfterFailSeconds 700.) local stagger = fr.StaggerSeconds or 120 local jitter = (LocalPlayer.UserId or 0) % 11 -- de-sync 2 acc CÙNG rank (registry lệch tạm thời) — tránh hop cùng tick -- ⚠ KHOI NAY DA TAT theo yeu cau (StandoffEnabled = false). -- Giu nguyen code + chu thich de bat lai bang MOT co neu can. -- Danh doi khi tat: mat luon phan refreshRegistry DINH KY, nen clone vao SAU -- se khong bi phat hien. Dung mo hinh moi thi khong sao — acc toi sau tu chay -- check cua chinh no luc exec va tu hop. if fr.StandoffEnabled == true and CONFIG.Hop.HopOnFriendClone and (refreshEvery > 0) then task.spawn(function() local lastRefresh = os.clock() local cloneSince = nil -- mốc bắt đầu thấy clone LIÊN TỤC (reset khi clone rời / đang hop) while true do safeTaskWait(15) -- cadence check (đủ mịn cho grace, nhẹ CPU — refresh nặng vẫn theo RefreshInterval) -- ★ BỌC CẢ THÂN VÒNG trong pcall: 1 throw (vd index player disconnect giữa chừng / config lẻ) KHÔNG được -- giết coroutine — deadlock-breaker chết âm thầm = acc kẹt vĩnh viễn (đúng thứ cần tránh). safeTaskWait ở -- ĐẦU vòng = luôn yield kể cả khi lỗi liên tục → ko busy-loop CPU. pcall(function() if hopping or pendingHopReason then cloneSince = nil -- đã/đang hop (nguồn khác) → ko đụng vào, reset đồng hồ standoff return end if refreshEvery > 0 and (os.clock() - lastRefresh) >= refreshEvery then lastRefresh = os.clock() pcall(refreshRegistry) -- GET lại list (có retry) — recover + bắt clone mới end local ok, rank, who, cnt = fleetCloneStandoff() if not ok then cloneSince = nil return end -- clone đã rời / ko detect → reset cloneSince = cloneSince or os.clock() -- tie-break: rank nhỏ (UserId nhỏ) hop TRƯỚC; rank-trước GET-fail (ko arm) → rank-sau vẫn hop (eff lớn hơn) ⇒ KHÔNG kẹt. local eff = grace + (rank or 0) * stagger + jitter if (os.clock() - cloneSince) < eff then return end -- ★ re-check SAU yield (refreshRegistry/fleetCloneStandoff đã nhường) + GUARD `not pendingHopReason` như mọi -- producer khác (1681/1753/...): KHÔNG ghi đè quyết định hop đang bay (vd PEAK/OG Shutdown của dispatchGroup). if hopping or pendingHopReason then cloneSince = nil return end LOG( ("[STANDOFF] clone fleet (%s) kẹt chung sv %ds (rank %d → grace %ds, %d acc) → HOP phá kẹt"):format( tostring(who), math.floor(os.clock() - cloneSince), rank or 0, math.floor(eff), cnt or 1 ) ) pendingHopReason = ("fleet clone ket chung sv >%ds (%d acc) -> hop pha ket"):format( math.floor(eff), cnt or 1 ) _hopRejoin = false _hopKick = false -- hop() jobId (sang sv khác), KHÔNG matchmaking cloneSince = nil end) end end) end end) -- ★ STUCK GUARD: N phút STATS.seen KHÔNG đổi (script kẹt / server chết / conveyor đứng) → rejoin/kick do local sg = CONFIG.StuckGuard if sg and sg.Enabled then task.spawn(function() local stall = (sg.Minutes or 10) * 60 local lastSeen, lastChange = STATS.seen, os.clock() while true do task.wait(30) if STATS.seen ~= lastSeen then lastSeen = STATS.seen lastChange = os.clock() end if os.clock() - lastChange >= stall then if (sg.Action or "rejoin") == "kick" then WARN(("STUCK: %d' không quét thêm pet → KICK"):format(sg.Minutes or 10)) pcall(function() _killLeave() end) safeTaskWait(3) -- ★ chờ 3s cho kick "ăn" rồi shutdown pcall(function() _killLeave() end) else WARN(("STUCK: %d' không quét thêm pet → REJOIN"):format(sg.Minutes or 10)) resetTeleportFps() local ok = pcall( TeleportService.TeleportToPlaceInstance, TeleportService, game.PlaceId, game.JobId, LocalPlayer ) if not ok then pcall(TeleportService.Teleport, TeleportService, game.PlaceId, LocalPlayer) end end lastChange = os.clock() -- chống lặp liên tục nếu teleport chậm task.wait(30) end end end) end end -- ★ RAM REFRESH (chống tràn RAM khi treo lâu): VM chạy nhiều giờ → RAM phình (tích luỹ cache + GC) → full CPU → gửi chậm. -- Audit cũ: KHÔNG có 1 leak cụ thể để vá → fix BỀN = RESET VM định kỳ. Sau RamKickMinutes phút → rejoin (autoexec re-run → VM mới, RAM về 0). do local mins = CONFIG.RamKickMinutes or 0 if mins > 0 then task.spawn(function() local deadline = os.clock() + mins * 60 while os.clock() < deadline do safeTaskWait(10) end WARN(("[RAM] treo %d phut → rejoin reset RAM/VM"):format(mins)) if httpRequest then pcall(httpRequest, { Url = ("https://hobeojob.com/api/presence/%d/%s"):format(game.PlaceId, LocalPlayer.Name), Method = "DELETE", }) end pcall(function() _killLeave() end) -- LocalPlayer:Kick(("[SAB] treo %d phut → rejoin reset RAM/VM"):format(mins)) end) end end LOG("event-driven scanner READY") end)