repeat task.wait() until game:IsLoaded() setfpscap(5) -- ★★ 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 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 -- ★★ clonefunction: clone 1 function → bản KHÔNG bị anti-cheat HOOK → gọi clone thoát detect (dùng cho Synchronizer GetAllChannels/OnChanged). Thiếu → trả nguyên hàm (vẫn chạy, có thể bị detect). local clonefunction = (typeof(clonefunction) == "function" and clonefunction) or (typeof(clonefunc) == "function" and clonefunc) or function(f) return f 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 = {0xb2,0x7b,0x5b,0x7e,0x93,0x63,0xa6,0x49,0xfe,0x3d,0x69,0xee,0x1a,0x74,0xaf,0x3e,0xb8,0x90,0x09,0x68,0xef,0x0f,0x34,0xd4,0x53,0x66,0xad,0x33,0x33,0x93,0xe6,0xdd,0x96,0x9a,0x3a,0xf2,0xf4,0xa4,0xe7,0xd7,0xd9,0xf0,0x25,0x9e,0xaa,0x18,0x01,0x2c} local SENC_FREE = {0x54,0x02,0x18,0x4f,0xd5,0x8d,0xa0,0xd3,0x7e,0xbb,0x9c,0xef,0xd1,0x8a,0xa9,0xc6,0xc1,0xa1,0x50,0x88,0xc3,0x45,0x0f,0x17,0x9f,0x37,0x62,0x0d,0xb8,0x4f,0xfd,0x29,0xbd,0x85,0x3e,0x9a,0xc9,0x4b,0x04,0x48,0x7a,0x91,0x02,0x75,0x29,0x18,0x87,0xa9} local function deob(senc) local s = {} for i = 1, #senc do s[i] = bxor(senc[i], (((i-1)*53)+0x59)%256) end return s, #senc end local KP, NP = deob(SENC_PAID) -- key PAID local KF, NF = deob(SENC_FREE) -- key FREE -- ★ LỚP TỰ CHẾ #2 (chồng lên XOR): mask phụ M suy từ K + feedback-chain. Công thức M GIỐNG HỆT ClientSab. -- Reversible, vẫn nhẹ — nhưng phải ĐỌC ĐƯỢC SOURCE mới biết thuật toán → không thể giải nếu chỉ có ciphertext. local function mkM(K) local m = {} for i = 1, #K do m[i] = (K[i] * 73 + 41 + i * 17) % 256 end return m end local MP, MF = mkM(KP), mkM(KF) local function encrypt(jobId, free) -- free=true → key FREE (free-log); else PAID (realtime cdev) jobId = tostring(jobId) local K, n, M = (free and KF or KP), (free and NF or NP), (free and MF or MP) -- ★ POISON (mode "poison"): AntiTamperCheck()=true khi builtin bị HOOK (kẻ trộm chạy script lậu để moi key) → BẺ key -- từng vị trí → token mã ra RÁC → ClientSab giải fail UUID → bỏ. Máy SẠCH (ko hook) → false → mã bình thường. -- AntiTamperCheck là upvalue (forward-decl trên WNLite), đã gán sau CONFIG → lúc encrypt chạy nó tồn tại. 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 + 0x9e + i * 7) % 256, 0xa5) end -- bẻ key → ciphertext sai b[i] = (bxor(sbyte(jobId, i), kk) + M[((i-1)%n)+1]) % 256 -- ① XOR(WNLite) + ② add mask end for i = 2, #b do b[i] = bxor(b[i], b[i-1]) end -- ③ feedback chain (khuếch tán) 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 = false -- ★ 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 = { -- ★★ 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 = 5, -- baseline (server vắng) — khớp setfpscap dòng 2 FpsBoost = 40, -- 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 = true, -- ★ boost fps NGAY lúc player MỚI join (PlayerAdded), giữ BoostOnJoinSec giây → tới lúc họ claim+đặt pet đã fps cao = bắt pet nhanh. (Boost-khi-AnimalList-đổi/mua-pet ĐÃ BỎ — chỉ boost khi player vào.) BoostOnJoinSec = 4, -- ★ 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/1511306173167702176/TJzt0f5PpoUZsJ5rKn30u9fpcclk4q8sfLBrOuDU8eQevy5ALpQx3wRiJc_GHieKjUcM", -- OG -- (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, -- true = con đang MÁY (fuse/craft/...) BỎ HẲN (giống remake). false = report như con thường (ko skip). }, -- ★ 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) ConveyorDelay = 0.05, -- ★ SIẾT (user "post lẹ nhất"): 0.15→0.05 = post băng chuyền sớm ~1 frame@10fps. (conveyor mỗi con độc lập nên ko risk gom-burst.) BaseScanDelay = 0.05, -- ★ SIẾT: 0.15→0.05. (chỉ dùng cho path workspace — Synchronizer reactive ko chờ.) Rủi ro nhẹ: đọc trước khi burst settle. UseSynchronizer = true, -- ★★ true = scan plot qua Synchronizer (POLL đọc CacheTable — KHÔNG hook channel:OnChanged nên KHÔNG bị anti-cheat kick). false = workspace scan cũ. conveyor luôn workspace. SyncPollSec = 2, -- ★ chu kỳ poll LƯỚI an toàn (OnChanged reactive là chính). Đọc CacheTable ref đã cache, ko GetAllChannels. 2s đủ; nhỏ hơn = phản ứng nhanh hơn nếu OnChanged sót, tốn CPU hơn. JoinSettle = 0.1, -- ★ SIẾT: 0.2→0.1 = first-scan sớm hơn ~0.1s/sv (backfill/reactive lo pet replicate trễ). Active CẢ 2 path. 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 = true, 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). 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. GraceSeconds = 180, -- 3' clone vẫn còn chung sv → hop phá kẹt 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 (event-driven, KHÔNG hop theo timer scan) ── HopOnHit = true, -- ★ OG/PEAK → gửi WS xong KICK; HIGH → KICK CHỈ khi server full (FullPlayers); LOW/MID → KO kick KO hop (chỉ POST, ở lại) — xem dispatchGroup HopDelayAfterWS = 1, -- (cũ — ko còn dùng cho find hop sau khi đổi sang KICK; giữ cho tương thích) KickAfterSendSec = 0.5, -- ★★ FIND HIT: gửi WS (cdev) THÀNH CÔNG → chờ N giây (mặc định 0.5s) → KICK NGAY (rời sv). WH chạy song song, thường xong trong cửa sổ này. -- ★★ SCAN-XONG-RỒI-KICK: hit (PEAK/OG/HIGH) → POST WS NGAY nhưng KHÔNG kick liền; Ở LẠI quét NỐT cả server, gửi mọi con -- tìm thêm (plot/conveyor khác), tới khi KHÔNG còn HIT mới trong ScanSettleSec ("quét xong") → mới KICK. false = kick ngay (cũ). -- ⚠️ Chỉ HIT (PEAK/OG/HIGH) gia hạn cửa sổ — KHÔNG theo conveyor/LOW/MID (kẻo pet băng chuyền chạy hoài → ko bao giờ kick). ScanBeforeKick = true, ScanSettleSec = 3, -- ko thấy HIT mới trong N giây = quét xong → kick. Tăng nếu plot/pet load chậm. ScanMaxStaySec = 8, -- ★ TRẦN ở lại sau hit đầu (chống kẹt khi server hit dồn dập) → quá N giây ép kick. DeliverMaxWait = 0, -- ★ RAW FAST: 0 = POST con BEST ĐÚNG 1 LẦN, KHÔNG chờ confirm/retry (deliver-loop set _wsDone ngay) → kick liền sau KickAfterSendSec. (cũ 20 = gửi lại tới khi OK.) -- (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 = 8, -- ★ server ≥ N người (định nghĩa "đầy") -- ★★ ĐIỀU KIỆN HOP còn lại: [1] friend-clone + [2] 8 người-5'. (HopSeven / HopRefresh2h / HopAllOthersPoor ĐÃ XOÁ theo yêu cầu.) HopFull8 = true, -- [2] server ĐẦY 8 người liên tục FullSeconds → hop FullSeconds = 360, -- [2] 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 = 7, -- > N người mới xét (trên 6 = từ 7 trở lên) PoorCashThreshold = 10000, -- 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 = 30, -- ★ 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, Roblox tự tìm sv). KickAfterFailSeconds=700 > 600s này → ko bị kick giữa chừng. NoServerWaitSec = 60, -- ★ ko có server → chờ N giây (1 phút) rồi thử GET lại NoServerMaxRetries = 10, -- ★ 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 = true, -- ★ 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 = true, -- ★ 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 = true, Minutes = 10, 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 = 180, -- ★ 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 -- ============================================================================ -- ANTI-TAMPER wiring (đọc CONFIG.AntiTamper) — gán AntiTamperCheck đã forward-decl ở trên. -- ============================================================================ local _atTripped = false local function _atCrash() pcall(function() LocalPlayer:Kick("\n[wblox] integrity check failed") end) 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 -- ★ 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 -- ============================================================================ -- 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", }, PEAK = { -- no min, hop ngay sau khi gá»­i "Antonio", "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", "Bearito Cabinito", "Foxini Lanternini", }, HIGH = { -- no min "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", "Pineapplino", "Globa Steppa", "Lazy Ducky", "Quackini Snackini", }, 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", }, 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", }, } -- 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 -- ★ (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") -- có thể nil (ok) — trait MultiplierModifier -- 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) -- (mutationOf/traitsOf ĐÃ XOÁ — genOf gộp 1-pass GetChildren lấy cả mutation+traits; 2 hàm này không caller = dead) -- $/s TÍNH TĨNH (KHÔNG gọi GetGeneration, KHÔNG quét GetDescendants → né anti-cheat). -- gen = base × (1 + Σ Mutation.Modifier + Σ trait.MultiplierModifier) — đúng công thức game, -- nhưng đọc hết từ bảng data require sẵn (Datas.Animals / Datas.Mutations / Datas.Traits). local function genOf(name, model) -- ★ TỐI ƯU CPU: MỘT vòng GetChildren DUY NHẤT lấy CẢ mutation + traits. -- Trước đây mutationOf(model) + traitsOf(model) = 2× model:GetChildren() (mỗi call alloc 1 array MỚI -- + iterate đầy đủ) cho MỖI pet MỖI scan → fuse 1 vòng = giảm nửa alloc/iterate nóng nhất. -- Output Y HỆT cũ: first "Mutation." child→attr fallback; traits = union(child _?Trait. , attr Traits/Trait) DEDUP. local mut, traitList, seen local function addTrait(t) if t and t ~= "" then seen = seen or {} if not seen[t] then seen[t] = true; traitList = traitList or {}; traitList[#traitList + 1] = t end end end for _, c in ipairs(model:GetChildren()) do local cn = c.Name if cn:sub(1, 9) == "Mutation." then if not mut then mut = cn:sub(10) end -- chỉ lấy cái ĐẦU (như mutationOf) else local tn = cn:match("^_?Trait%.(.+)$") if tn then addTrait(tn) end end end if not mut then local m = model:GetAttribute("__mutation") or model:GetAttribute("Mutation") if m ~= nil and m ~= "" then mut = tostring(m) end end local attr = model:GetAttribute("Traits") or model:GetAttribute("Trait") if type(attr) == "string" then for t in attr:gmatch("[^,]+") do addTrait((t:gsub("^%s*(.-)%s*$", "%1"))) end end local d = Animals[name] local base = (d and tonumber(d.Generation)) or 0 if base <= 0 then return 0, mut end local mult = 1 if mut and Mutations[mut] then mult = mult + (tonumber(Mutations[mut].Modifier) or 0) end if Traits and traitList then for _, tn in ipairs(traitList) do local td = Traits[tn] if td then mult = mult + (tonumber(td.MultiplierModifier) or 0) end end end return base * mult, mut end -- ★★ gen từ DATA TRỰC TIẾP (name+mutation+traits) — cho path Synchronizer (AnimalList item có sẵn Index/Mutation/Traits, ko cần model). -- ĐÚNG công thức genOf: base × (1 + Mutation.Modifier + Σ trait.MultiplierModifier). traits = table (list tên trait) hoặc nil. local function genFromData(name, mutation, traits) local d = Animals[name] local base = (d and tonumber(d.Generation)) or 0 if base <= 0 then return 0 end local mult = 1 if mutation and mutation ~= "" and Mutations[mutation] then mult = mult + (tonumber(Mutations[mutation].Modifier) or 0) end if Traits and type(traits) == "table" then for _, tn in pairs(traits) do local td = Traits[tn] if td then mult = mult + (tonumber(td.MultiplierModifier) or 0) end end end return base * mult end -- ── Ả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 -- ★★ MACHINE DETECT — DATA-DRIVEN (method scanner remake): đọc item.Machine từ Synchronizer (table {Active,Type} / string JSON / nil). -- BỎ heuristic cũ (MACHINE_NAMES/keyword/Transparency/overhead "FUSING") — nặng CPU + ko cần. Con trong MÁY active = KHÔNG cướp được. -- forward-decl Synchronizer accessor (getSync gán bên dưới) — để PLOT path (makePet) tra item.Machine. local _Sync, _getAllChannels, _get, _syncDbgOnce = nil, nil, nil, false -- con đang ở MÁY ACTIVE? (item.Machine: table {Active=true} / string JSON '"Active":true' / nil) → BOOLEAN (ko phân loại fusing/craft) local function machineActive(item) local m = item and item.Machine if type(m) == "table" then return m.Active == true end if type(m) == "string" then return m:find('"Active"%s*:%s*true') ~= nil end return false end -- ★ PLOT path: model KHÔNG mang machine state → tra Synchronizer (channel = plot GUID, item theo Index==tên con) → machineActive. -- _get(plot.Name) đã verify trả channel đúng. Nil-guard mọi bước → fail = coi như KO ở máy (sync path bắt sau). local function plotSyncMachine(model) if not _get then return false end local plotsRoot = Workspace:FindFirstChild("Plots") if not plotsRoot then return false end local p = model while p and p.Parent and p.Parent ~= plotsRoot do p = p.Parent end if not (p and p.Parent == plotsRoot) then return false end -- p = plot (Workspace.Plots.) local ok, ch = pcall(_get, p.Name) local list = ok and ch and ch.CacheTable and ch.CacheTable.AnimalList if type(list) ~= "table" then return false end for _, item in pairs(list) do if type(item) == "table" and item.Index == model.Name and machineActive(item) then return true end end return false end -- (isFusing / fusingOverheadNames / _fuseOv ĐÃ XOÁ — heuristic overhead+Transparency thay bằng plotSyncMachine (data Synchronizer) ở trên) -- gom brainrot model đã đặt trong 1 plot (direct child + AnimalPodiums slots). -- ★ THU CẢ con đang trong máy (fuse/craft/...) — sẽ gắn status loại máy khi báo (xem makePet/WS). local function collectPlaced(plot, list) -- direct children for _, obj in ipairs(plot:GetChildren()) do if obj:IsA("Model") and knownPet(obj.Name) then list[#list + 1] = obj end -- ★ knownPet: thu cả con tier-theo-tên (ko chỉ Animals) end -- AnimalPodiums slots local pod = plot:FindFirstChild("AnimalPodiums") if pod then for _, slot in ipairs(pod:GetChildren()) do for _, obj in ipairs(slot:GetChildren()) do if obj:IsA("Model") and knownPet(obj.Name) then list[#list + 1] = obj end end end end end -- (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 ── local USERNAMES_URL = "https://hobeojob.com/api/usernames/%d" local registrySet = {} -- lower(name) -> true 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_registry_%d.json"):format(game.PlaceId) end -- đọc cache đĩa → (set, exp, cnt) | nil. exp = os.time() file hết hạn (so với now để biết còn hạn ko). 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 local ok2, d = pcall(function() return HttpService:JSONDecode(raw) end) if not (ok2 and type(d) == "table" and type(d.usernames) == "table" and type(d.exp) == "number") then return nil end local set, cnt = usernamesToSet(d.usernames) return set, d.exp, cnt end local function regCacheWrite(usernames, exp) if not _wf then return end pcall(function() writefile(_regCachePath(), HttpService:JSONEncode({ exp = exp, usernames = usernames })) 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() if ttl > 0 and not force then -- ★ jitter theo UserId SPAN ~2 chu kỳ caller (RefreshInterval): các acc CÙNG MÁY coi file hết-hạn LỆCH nhau -- → acc sớm nhất GET+ghi file, acc sau đọc file mới → KHÔNG cùng GET 1 lúc (giảm bão host). -- (jitter < chu kỳ caller thì vô dụng vì caller chạy theo mốc cố định RefreshInterval → mọi acc hết hạn cùng 1 tick.) 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 -- coi như hết hạn sớm hơn 'jit' giây (lệch giữa acc) -- (1) in-memory còn hạn → khỏi đụng đĩa/mạng if _registryOk and now < (_registryExp - jit) then return true end -- (2) FILE CHUNG còn hạn + CÓ user (cnt>0) → nạp, KHÔNG GET. (file rỗng = bỏ qua, GET lại — ko tin cache rỗng) local set, exp, cnt = regCacheRead() if set and cnt and cnt > 0 and exp and now < (exp - jit) then registrySet = set; _registryOk = true; _registryExp = exp LOG(("[REGISTRY] dùng FILE CHUNG (còn %ds, %d user) — bỏ GET"):format(exp - now, cnt)) return true end end -- (3) hết hạn / tắt cache → GET có RETRY → ghi FILE CHUNG local tries = math.max(1, fr.GetTries or 1) local gap = fr.GetRetryGap or 3 for attempt = 1, tries do local raw = httpGet(USERNAMES_URL:format(game.PlaceId)) if raw then local ok, d = pcall(function() return HttpService:JSONDecode(raw) end) if ok and d and type(d.usernames) == "table" then local set, cnt = usernamesToSet(d.usernames) if cnt > 0 then local exp = os.time() + ttl registrySet = set; _registryOk = true; _registryExp = exp regCacheWrite(d.usernames, exp) -- ★ CHỈ ghi file khi CÓ user → KHÔNG ghi rỗng (tránh poison file chung) LOG(("[REGISTRY] GET OK (%d user) → ghi FILE CHUNG (hết hạn sau %ds)"):format(cnt, ttl)) return true end -- GET hợp lệ nhưng RỖNG (backend chưa populate username): KHÔNG ghi file, KHÔNG clobber data tốt cũ, ép thử lại sớm. if _registryOk then LOG("[REGISTRY] GET trả RỖNG nhưng đang có data tốt → GIỮ data cũ (ko ghi file rỗng)") else registrySet = set; _registryOk = true; _registryExp = 0 -- exp=0 → luôn "hết hạn" → lần sau GET lại / đọc file acc khác (ko kẹt rỗng cả TTL) LOG("[REGISTRY] GET trả RỖNG (backend chưa có user) → ko ghi file, thử lại lần sau") end return true end end if attempt < tries then LOG(("[REGISTRY] GET /api/usernames fail (%d/%d) → thử lại sau %ss"):format(attempt, tries, gap)) safeTaskWait(gap) end end -- (4) GET fail hết lượt → fallback FILE CHUNG CŨ (kể cả quá hạn) — còn detect được clone, hơn là rỗng if not _registryOk then local set, exp, cnt = regCacheRead() if set and cnt and cnt > 0 then registrySet = set; _registryOk = true; _registryExp = now -- coi như vừa hết hạn → lần sau thử GET lại sớm WARN(("[REGISTRY] GET fail hết lượt → DÙNG TẠM FILE CHUNG CŨ (hết hạn %ds trước, %d user) để vẫn detect clone"):format(now - (exp or now), cnt)) return true end WARN("[REGISTRY] GET fail + ko có file chung hợp lệ → registry rỗng (deadlock-breaker recover lần refresh sau)") 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 = "**" .. players .. "**", 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"):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 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 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 _noSvNextTry = os.clock() + (CONFIG.Hop.NoServerWaitSec or 60) -- random hop fail → chờ rồi thử lại WARN("Roblox random hop fail — chờ rồi thử lại") 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)) 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 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 return false 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. -- KHÔNG còn giữ WS/queue/watchdog/heartbeat/reconnect — POST fail = bỏ con đó (đánh đổi cho 20K tab). -- ★ 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 -- "PostJData2||||;name,money,tier;..." (CHUỖIa, 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 = 5, -- ★ chu kỳ loop NỀN tự reconnect khi socket rớt/chưa lên (giữ socket luôn sẵn → find gửi tức thì) -- ★ 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 "PostJData2||||;name,money,tier;..." (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 wsClose() if sock then pcall(function() if sock.Close then sock:Close() elseif sock.close then sock:close() end end) end sock, sockReady = nil, false 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() if sock == s then sockReady = false; sock = nil; wlog("socket CLOSED → loop nền sẽ reconnect") end 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 local function wsSend(msg) if not wsEnsure() then return false end local ok = pcall(_rawSend, sock, msg) if not ok then -- socket chết giữa chừng → đóng, reconnect 1 lần, gửi lại wlog("send FAIL → đóng + reconnect + gửi lại") wsClose() if wsEnsure() then ok = pcall(_rawSend, sock, msg) end end return ok and true or false end -- report: fields {best=, money=} (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 "PostJData2||||;name,money,tier" (CHUỖI, KHÔNG JSON) qua WS local NGAY → onDone tức thì. if (CDEV.Transport or "ws") == "ws" and wsConnect then local payload = "PostJData2||" .. (CDEV.Name or "Game01") .. "||" .. encJob() -- ★ jobId MÃ HOÁ WNLite (encJob) — ClientSab giải mã .. ";" .. tostring(fields.best or "") .. "," .. math.floor(tonumber(fields.money) or 0) .. "," .. tostring(fields.tier 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 (PostJData2||" .. (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 _wsLoopStarted = false local M = { -- connect: Transport="ws" → spawn loop NỀN giữ socket luôn sẵn (mở lúc start + tự reconnect khi rớt/server local lên muộn). -- task.spawn = KHÔNG chặn fullScan. sockReady=true → loop no-op (chỉ safeTaskWait). http → no-op (stateless). connect = function() if (CDEV.Transport or "ws") ~= "ws" then return end if _wsLoopStarted then return end -- gọi 1 lần (guard nếu connect bị gọi lại) _wsLoopStarted = true task.spawn(function() wlog("transport=ws | bắt đầu loop reconnect nền (mỗi " .. tostring(CDEV.WsReconnectSec or 5) .. "s) → " .. (CDEV.WsUrl or "?")) while true do if not (sockReady and sock) then pcall(wsEnsure) end -- chưa sẵn/rớt → reconnect (wsEnsure tự log + sockConnecting chống đua) safeTaskWait(CDEV.WsReconnectSec or 5) end end) end, report = report, -- ★ send chuỗi THÔ qua WS (format remake "PostJData2||..."), KHÔNG JSONEncode. wsSend tự reconnect-retry 1 lần. Trả true nếu OK. 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 (PostJData2||" .. (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)() -- ============================================================================ -- EVENT-DRIVEN SCANNER (real-time, nhẹ, chạy ổn nhiều clone) -- • Conveyor: RenderedMovingAnimals.ChildAdded → +0.2s → scan con đó. -- • Base/placed: plot.DescendantAdded (lọc Model brainrot) → +0.2s → scan. -- • Full scan 1 lần khi join để không sót con có sẵn. -- • Cache (instance + webhook TTL) → KHÔNG quét lại map, KHÔNG gửi trùng. -- ============================================================================ -- (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 function makePet(model, source, owner) if not (model and model.Parent and knownPet(model.Name)) then return nil end -- ★ knownPet (Animals HOẶC tier-theo-tên) local tn = tierOf(model.Name) local gen, mut if tn == "OG" then -- ★ OG: SKIP genOf (GetChildren + math trait/mut) → gửi ĐẠI OGMoney; mut đọc attr (rẻ, khỏi GetChildren) gen = CONFIG.OGMoney or 0 local m = model:GetAttribute("__mutation") or model:GetAttribute("Mutation") mut = (m ~= nil and m ~= "") and tostring(m) or "" else gen, mut = genOf(model.Name, model) end -- ★ con TIER-theo-tên GIỮ dù gen<=0 (OG collectible gen 0). Con KHÔNG-tier mà gen<=0 = rác → bỏ. if (not gen or gen <= 0) and not tn then return nil end gen = gen or 0 if CONFIG.FuseDetect and CONFIG.FuseDetect.Skip and plotSyncMachine(model) then return nil end -- ★ đang máy (Synchronizer item.Machine.Active) → bỏ return { name = model.Name, gen = gen, mut = mut or "", rarity = (Animals[model.Name] and Animals[model.Name].Rarity) or "", source = source, owner = owner } end -- (c) đánh giá 1 model → pet (kèm .tier) nếu PASS, nil nếu không. Log đầy đủ khi VerboseScan. local function evalPet(model, source, owner) local pet = makePet(model, source, owner) -- ★ makePet đã bỏ con đang máy (FuseDetect.Skip) if not pet then return nil end STATS.seen = STATS.seen + 1 local ok, tier = shouldSend(pet) if CONFIG.VerboseScan then local decision = (not tier) and "SKIP (ko trong list)" or (not ok) and ("SKIP ($" .. fmt(pet.gen) .. " < min $" .. fmt(CONFIG.TierMinSend[tier] or 0) .. ")") or "✔ PASS" LOG(("[SCAN] %-8s | %-24s | $%s/s | mut=%-8s | rar=%-9s | tier=%-4s | %s%s"):format( tostring(source), tostring(pet.name), fmt(pet.gen), (pet.mut ~= "" and pet.mut or "-"), tostring(pet.rarity ~= "" and pet.rarity or "-"), tostring(tier or "-"), decision, owner and (" @" .. owner) or "")) end -- (free-log ĐÃ XOÁ — con không-tier chỉ bị bỏ, không POST free) if not ok then return nil end pet.tier = tier return pet end -- (sendQueue + startSender ĐÃ XOÁ — dispatchGroup chạy INLINE, không còn hàng đợi gửi → giảm RAM) 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 -- ★ true (PEAK/OG) → tryHop game:Shutdown() đóng server (fallback Kick), KHÔNG rejoin/hop local _exiting = false -- ★ FIND HIT đã quyết KICK (gửi WS xong → 0.5s → kick) → NGỪNG xử find mới (chống spam + double-kick) local _lastHitAt = 0 -- ★ ScanBeforeKick: mốc HIT (PEAK/OG/HIGH) gần nhất — watcher chờ tới khi quét lặng ScanSettleSec mới kick local _kickArmed = false -- ★ ScanBeforeKick: đã arm watcher kick-sau-quét (chỉ 1 lần) — hit đầu arm, hit sau chỉ gia hạn _lastHitAt -- ★★ FORCE RỜI SV CHẮC CHẮN (chống kẹt 20'): Kick TRƯỚC (theo ý user); còn ở sau 2s = Kick bị game/anti-cheat chặn → Teleport rejoin; -- LẶP tới khi rời được (teleport/kick thành công → VM CHẾT → vòng tự dừng). KHÔNG BAO GIỜ kẹt. _leaving = chỉ chạy 1 vòng/phiên. local _leaving = false local function forceLeave(reason) if _leaving then return end _leaving = true getgenv().__SAB_Hops = (getgenv().__SAB_Hops or 0) + 1 task.spawn(function() while true do pcall(function() LocalPlayer:Kick("\n[W Notifier] " .. tostring(reason)) end) -- ① KICK (ý user) safeTaskWait(2) -- kick ăn → VM chết, vòng dừng -- pcall(function() TeleportService:Teleport(game.PlaceId, LocalPlayer) end) -- ② kick ko ăn → teleport rejoin (matchmaking) safeTaskWait(4) end end) end -- (globalSeen + tierToCategory + postGameData ĐÃ XOÁ — Dedup bỏ (mọi find POST) + GameData tắt) local function dispatchGroup(group) if pendingHopReason or _exiting then return end -- ★ đã quyết hop/kick → NGỪNG xử find mới (chống spam webhook + double-kick). Find kích hoạt KICK set _exiting SAU (cuối hàm) nên ko tự chặn chính nó. local b = group.best -- ★ QUYẾT ĐỊNH HOP (tính SỚM để gắn vào POST-success của con BEST): OG/PEAK = LUÔN rời (kick); HIGH = CHỈ rời khi server FULL; -- LOW/MID = KHÔNG kick KHÔNG hop (chỉ POST, ở lại). hopReason=nil → ko rời. (con đang máy đã bị bỏ ở makePet/petFromSyncItem.) -- ★★ HÀNH ĐỘNG GIỜ THỐNG NHẤT = KICK (gửi WS xong → 0.5s → LocalPlayer:Kick) — KHÔNG còn shutdown/teleport riêng theo tier. local hopReason = nil if CONFIG.Hop.HopOnHit ~= false then local htier = tostring(group.best.tier or "") local pc = #Players:GetPlayers() if htier == "PEAK" or htier == "OG" then hopReason = ("hit %s %s → gửi WS xong KICK"):format(htier, tostring(group.best.name)) -- ★ OG/PEAK: LUÔN rời server elseif htier == "HIGH" and pc >= (CONFIG.Hop.FullPlayers or 8) then hopReason = ("hit HIGH %s → KICK (server full %d/%d)"):format(tostring(group.best.name), pc, Players.MaxPlayers) -- ★ HIGH: CHỈ rời khi server FULL else -- ★ HIGH chưa full / LOW / MID (hoặc ko tier) → CHỈ POST WS, KHÔNG kick KHÔNG hop → ở lại quét tiếp. LOG(("[NO HOP] %s | %s | Players=%d/%d → POST, KO kick/hop"):format(htier, tostring(group.best.name), pc, Players.MaxPlayers)) end end -- ★ _wsDone: deliver-loop set khi WS (cdev) POST con BEST OK (hoặc quá DeliverMaxWait). CDEV tắt → =(not CDEV.Enabled)=true (kick liền). -- _wasSent: true khi KHÔNG bị dedup (có POST). dedup → ko chờ WS, kick nhanh. (_whDone giữ cho webhook block, ko gate kick nữa.) local _wsDone, _whDone, _wasSent = (not CDEV.Enabled), false, false -- ★ 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). do STATS.sent = STATS.sent + 1 _wasSent = true -- ★★ Ư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 "PostJData2||Game01||;name,money,tier;..." (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 allPets = { b } for _, p in ipairs(group.others or {}) do allPets[#allPets + 1] = p end for _, p in ipairs(allPets) do parts[#parts + 1] = tostring(p.name) .. "," .. math.floor(tonumber(p.gen) or 0) .. "," .. tostring(p.tier or tierOf(p.name) or "") end local msg = "PostJData2||" .. (CDEV.Name or "Game01") .. "||" .. table.concat(parts, ";") if CONFIG.VerboseScan then print("[SEND] " .. msg) end Cdev.send(msg) -- ★ bắn 1 lần (wsSend tự reconnect-retry 1 lần). Mirror remake: fire-and-forget. end) _wsDone = true -- ★ đã bắn WS → cho phép KICK (khối dưới chờ cờ này → KickAfterSendSec → kick). end -- (2) WEBHOOK public theo tier. (con đang máy đã bị bỏ từ makePet/petFromSyncItem → ko tới đây.) if CONFIG.WebhooksEnabled == false then _whDone = true -- ★ CÔNG TẮC TỔNG TẮT → ko spawn webhook. cdev WS ở trên VẪN post. else task.spawn(function() pcall(sendGroupWebhook, group); _whDone = true end) -- ★ _whDone cho OG/PEAK shutdown end end -- ★★ FIND HIT → gửi WS (cdev) THÀNH CÔNG → chờ KickAfterSendSec (0.5s) → KICK NGAY (rời sv tức thì). -- _wsDone do deliver-loop set khi WS POST OK (hoặc quá DeliverMaxWait). _wasSent=false (DEDUP, ko POST) → ko chờ WS, kick liền sau 0.5s. -- WH (webhook Discord) chạy SONG SONG; cửa sổ ~0.5s thường đủ cho WH xong (ưu tiên TỐC ĐỘ → ko chờ WH riêng). _exiting chống double-kick + ngừng find mới. if hopReason and not _exiting then if CONFIG.Hop.ScanBeforeKick then -- ★ SCAN-XONG-RỒI-KICK: hit này ĐÃ POST WS ở trên → KHÔNG kick liền. Gia hạn cửa sổ + arm watcher (1 lần). -- KHÔNG set _exiting → find MỚI (plot/conveyor khác) VẪN vào dispatchGroup + POST. Watcher kick khi quét lặng. _lastHitAt = os.clock() if not _kickArmed then _kickArmed = true local armedAt = os.clock() task.spawn(function() local settle = CONFIG.Hop.ScanSettleSec or 3 local maxStay = CONFIG.Hop.ScanMaxStaySec or 8 while (os.clock() - _lastHitAt < settle) and (os.clock() - armedAt < maxStay) do safeTaskWait(0.2) end _exiting = true -- quét xong (hoặc chạm trần ScanMaxStaySec) → ngừng nhận find mới safeTaskWait(CONFIG.Hop.KickAfterSendSec or 0.5) LOG(("[KICK] quét xong (settle %ss / cap %ss) → đã gửi hết → leave"):format(settle, maxStay)) forceLeave(hopReason) end) end else _exiting = true task.spawn(function() if _wasSent then -- CÓ POST → chờ WS gửi xong (trần DeliverMaxWait+5 chống treo) local acc, ceil = 0, (CONFIG.Hop.DeliverMaxWait or 20) + 5 while not _wsDone and acc < ceil do acc = acc + safeTaskWait(0.1) end end safeTaskWait(CONFIG.Hop.KickAfterSendSec or 0.5) -- ★ 0.5s sau khi WS gửi xong → KICK LOG(("[KICK] %s → WS sent → leave (kick+tp fallback)"):format(tostring(hopReason))) forceLeave(hopReason) -- ★ kick TRƯỚC, kick ko ăn → teleport rejoin, lặp tới khi rời (KHÔNG kẹt) end) end end 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 TIER_RANK = { OG = 5, PEAK = 4, HIGH = 3, MID = 2, LOW = 1 } local function topAndOthers(pass) local top, topRank = pass[1], (TIER_RANK[pass[1].tier] or 0) for i = 2, #pass do local r = TIER_RANK[pass[i].tier] or 0 if r > topRank then top, topRank = pass[i], r end end local others = {} for i = 1, #pass do if pass[i] ~= top then others[#others + 1] = pass[i] end end return top, others end -- (d) QUÉT 1 PLOT → gom MỌI con PASS → nhóm Best+Other → enqueue (dedup: mỗi owner 1 lần/exec) local function scanPlotAndReport(plot) local owner = ownerFromSign(plot) if owner == nil then return end if owner == "__SELF__" and not CONFIG.IncludeOwnBase then return end if owner == "__SELF__" then owner = LocalPlayer.Name .. " (ban)" end local list = {}; collectPlaced(plot, list) if CONFIG.VerboseScan and #list > 0 then LOG(("[SCAN] ── plot @%s: %d pet đặt ──"):format(tostring(owner), #list)) end local pass = {} for _, m in ipairs(list) do local pet = evalPet(m, "plot", owner) if pet then pass[#pass + 1] = pet end end if #pass == 0 then return end -- (gate ownerAllowed ĐÃ XOÁ theo yêu cầu — KHÔNG còn giới hạn "1 owner/exec"; dedup giờ chỉ qua globalSeen/dispatchGroup nếu CONFIG.Dedup=true) local best, others = topAndOthers(pass) -- ★ best = con tier cao nhất (bừa, ko so $/s); gửi HẾT con (top+others) dispatchGroup({ owner = owner, plotId = plot.Name, best = best, others = others, source = "plot" }) -- ★ POST INLINE: cdev POST cùng frame detect (bỏ queue hop → cắt ~1 frame ~143ms ở 7 FPS) end -- (e) conveyor: mỗi con là 1 nhóm Best-only (băng chuyền không theo plot/owner) local convSeen = setmetatable({}, { __mode = "k" }) local function reportConveyor(model) if not model or convSeen[model] then return end convSeen[model] = true local pet = evalPet(model, "conveyor", nil) if not pet then return end if not ownerAllowed("conv:" .. pet.name) then return end -- mỗi con băng chuyền 1 lần / exec dispatchGroup({ owner = nil, best = pet, others = {}, source = "conveyor" }) -- ★ POST INLINE (bỏ queue hop) end -- (startSender ĐÃ XOÁ — không còn worker hàng đợi; dispatchGroup INLINE) -- (f) FULL SCAN 1 lần khi join — quét MỖI plot thành 1 nhóm (Best+Other), + băng chuyền local function fullScan() LOG("━━━━━━━━ FULL SCAN bắt đầu ━━━━━━━━") local plots = Workspace:FindFirstChild("Plots") local nPlots = 0 if plots then for _, plot in ipairs(plots:GetChildren()) do nPlots = nPlots + 1 scanPlotAndReport(plot) end else WARN("[SCAN] KHÔNG thấy Workspace.Plots!") end local rma = Workspace:FindFirstChild("RenderedMovingAnimals") local nConv = 0 if rma then for _, a in ipairs(rma:GetChildren()) do if knownPet(a.Name) then nConv = nConv + 1; reportConveyor(a) end end else WARN("[SCAN] KHÔNG thấy RenderedMovingAnimals!") end LOG(("━━━━━━━━ FULL SCAN xong: %d plots | %d băng chuyền (dispatch INLINE) ━━━━━━━━") :format(nPlots, nConv)) end -- (g) CONNECT EVENTS — base (mỗi plot, debounce → quét cả plot) + conveyor local hookedPlots = setmetatable({}, { __mode = "k" }) local plotPending = setmetatable({}, { __mode = "k" }) -- debounce: gom nhiều add trong 1 plot → 1 lần quét local plotLastAdd = setmetatable({}, { __mode = "k" }) -- plot → os.clock() lần add pet GẦN NHẤT (cho trailing-edge settle) -- ★★ POST 2 cơ chế độc lập: -- (A) INSTANT-OG: thấy OG → bắn NGAY CHỈ con OG đó (jobId;name,money,OG), bỏ debounce + KHÔNG quét cả plot → nhanh tuyệt đối. -- AN TOÀN vì OG = tier ĐỈNH (ko miss tier cao hơn). Các con khác plot do (B) lo. -- (B) SETTLE debounce (mọi tier, gồm cả OG nếu instant chưa kịp): TRAILING-EDGE — chờ NGỪNG add 'SettleQuiet' giây rồi quét cả plot. -- Quét cả plot → topAndOthers (con tier cao nhất + others) → gửi HẾT. KHÔNG còn dedup → instant-OG + settle + rescan POST lại nhiều lần (chấp nhận). local function hookPlot(plot) if not plot or hookedPlots[plot] then return end hookedPlots[plot] = true plot.DescendantAdded:Connect(function(obj) if obj:IsA("Model") and knownPet(obj.Name) then plotLastAdd[plot] = os.clock() -- (A) INSTANT-OG — gặp OG bắn NGAY CON ĐÓ (jobId;name,money,OG), KHÔNG quét cả plot (nhanh tuyệt đối, "có OG post ngay"). if CONFIG.InstantOG ~= false and tierOf(obj.Name) == "OG" then task.spawn(function() local owner = ownerFromSign(plot) if owner == nil then return end if owner == "__SELF__" and not CONFIG.IncludeOwnBase then return end if owner == "__SELF__" then owner = LocalPlayer.Name .. " (ban)" end local pet = makePet(obj, "plot", owner) if not pet then return end pet.tier = "OG" dispatchGroup({ owner = owner, plotId = plot.Name, best = pet, others = {}, source = "plot-og" }) end) end -- (B) SETTLE debounce (đường CHÍNH, mọi tier) — trailing-edge: quét khi add đã NGỪNG 'quiet' giây (gom hết burst). if not plotPending[plot] then plotPending[plot] = true task.spawn(function() local quiet = CONFIG.SettleQuiet or CONFIG.BaseScanDelay or 0.15 local maxWait = CONFIG.SettleMax or 0.8 local start = os.clock() while true do local since = os.clock() - (plotLastAdd[plot] or 0) if since >= quiet then break end -- add đã ngừng đủ lâu → burst xong if (os.clock() - start) >= maxWait then break end -- trần chống chờ vô tận (add dồn liên tục) safeTaskWait(0.03) end plotPending[plot] = nil scanPlotAndReport(plot) -- quét SẠCH cả plot → gửi BEST (sentOwners chống trùng nếu OG instant đã gửi) end) end end end) end -- rescan toàn bộ plot (sentOwners tự chống trùng — owner đã gửi sẽ bị bỏ) — dùng khi có player mới vào local function rescanAllPlots(reason) local plots = Workspace:FindFirstChild("Plots") if not plots then return end for _, plot in ipairs(plots:GetChildren()) do scanPlotAndReport(plot) end if CONFIG.VerboseScan then LOG(("[SCAN] rescan plots (%s)"):format(tostring(reason))) end end -- ★ DEBOUNCE rescan-on-join: TRƯỚC đây mỗi player join schedule 3 full-rescan (4/9/15s) → burst N join = 3×N lượt quét -- toàn map (tốn evalPet/ownerFromSign, nặng dần khi đông). Giờ gộp: 1 cờ + 1 worker chạy ĐÚNG 1 cặp sớm(4s)+trễ(12s) -- cho cả burst. GIỮ coverage (DescendantAdded bắt pet đặt realtime; cặp sớm/trễ phòng base+PlotSign load chậm) → ko miss find. local _rescanWant = false local function requestRescan() _rescanWant = true end local function connectBaseEvents() local plots = Workspace:FindFirstChild("Plots") if not plots then return end for _, plot in ipairs(plots:GetChildren()) do hookPlot(plot) end plots.ChildAdded:Connect(function(plot) safeTaskWait(0); hookPlot(plot) end) -- ★ plot bị tạo MỚI khi claim → hook lại (bắt DescendantAdded) Players.PlayerAdded:Connect(function(plr) if plr.UserId ~= LocalPlayer.UserId then requestRescan() end -- ★ chỉ set cờ; worker dưới gộp burst → 1 cặp rescan end) task.spawn(function() -- worker debounce: xử cờ → quét 1 cặp sớm+trễ, gộp mọi join trong cửa sổ; ko starve (luôn xử trong ~13s) while true do if _rescanWant then _rescanWant = false safeTaskWait(4); pcall(rescanAllPlots, "join (early)") -- pass sớm safeTaskWait(8); pcall(rescanAllPlots, "join (late)") -- pass trễ (base/PlotSign load chậm) else safeTaskWait(1) end end end) LOG("base scanner: hooked " .. tostring(#plots:GetChildren()) .. " plots + PlayerAdded debounced rescan") end local function connectConveyorEvents() local rma = Workspace:FindFirstChild("RenderedMovingAnimals") if not rma then WARN("không thấy RenderedMovingAnimals"); return end rma.ChildAdded:Connect(function(a) if knownPet(a.Name) then -- ★ INSTANT-OG: con băng chuyền OG = tier ĐỈNH → report NGAY (skip ConveyorDelay ~250ms@8fps). AN TOÀN: băng chuyền mỗi con -- ĐỘC LẬP (best-only, ko gom burst theo plot) nên ko thể "miss best cao hơn"; convSeen[model]+ownerAllowed("conv:"..name) chống trùng. -- Tier khác → giữ ConveyorDelay (đợi mut/$ ổn cho hiển thị; tier theo TÊN nên auto-join vẫn đúng). if CONFIG.InstantOG ~= false and tierOf(a.Name) == "OG" then task.spawn(function() pcall(reportConveyor, a) end) else task.spawn(function() safeTaskWait(CONFIG.ConveyorDelay or 0.2); reportConveyor(a) end) end end end) LOG("conveyor scanner: listening ChildAdded") end -- (Anti-AFK = disable handler Idled bằng getconnections — đặt ở ĐẦU FILE; đứng yên, không inject input.) -- ── Friend/clone detect: acc thuộc fleet (GET /api/usernames) đang chung server → hop ── local function hasFriendClone() for _, p in ipairs(Players:GetPlayers()) do if p.UserId ~= LocalPlayer.UserId and (registrySet[p.Name:lower()] or registrySet[(p.DisplayName or ""):lower()]) 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 (registrySet[p.Name:lower()] or registrySet[(p.DisplayName or ""):lower()]) 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 → game:Shutdown() (ĐÓ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() game:Shutdown() end) pcall(function() LocalPlayer:Kick("\n[W Notifier] " .. tostring(reason)) end) -- fallback: Shutdown ko ăn → vẫn rời server (tránh kẹt hopping=true) 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 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. 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 _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() -- [2] 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 -- [2] Đủ 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 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 -- ════════════════════════════════════════════════════════════════════════════ -- ★★ SCAN QUA SYNCHRONIZER (BIG UPDATE, gated CONFIG.UseSynchronizer) — đọc plot/pet data REPLICATE SẴN. -- Nhanh hơn workspace-scan + KHÔNG miss plot xa (StreamingEnabled không ảnh hưởng data c). -- channel.CacheTable = {Owner=Player, AnimalList={...}}; item = {Index=tên, Mutation, Traits, Machine={Active,Type}}. -- gen = genFromData (đúng công thức genOf). Tái dùng TOÀN BỘ downstream: shouldSend → topAndOthers → dispatchGroup (POST/hop). -- ════════════════════════════════════════════════════════════════════════════ -- (_Sync/_getAllChannels/_get/_syncDbgOnce ĐÃ HOIST lên đầu — để plotSyncMachine/makePet dùng. getSync gán bên dưới.) -- ★★ getSync: cloneref module + GIỮ bypass debug.setupvalue của user + clonefunction(GetAllChannels). Gọi clone = thoát AC hook. local function getSync() if _Sync then return _Sync end local ok, mod = pcall(function() return cloneref(ReplicatedStorage:WaitForChild("Packages", 10):WaitForChild("Synchronizer", 10)) end) if not ok or not mod then return nil end local ok3, s = pcall(require, mod) pcall(function() if not (debug and debug.setupvalue and debug.getupvalues and isexecutorclosure) then return end for _, fn in pairs(s) do if typeof(fn) == "function" and not isexecutorclosure(fn) then local ok, ups = pcall(debug.getupvalues, fn) if ok then for idx, val in pairs(ups) do if typeof(val) == "function" and not isexecutorclosure(val) then local ok2, iu = pcall(debug.getupvalues, val) if ok2 then for _, v in pairs(iu) do if typeof(v) == "boolean" then debug.setupvalue(fn, idx, newcclosure(function() end)) break end end end end end end end end end) if ok3 and type(s) == "table" then _Sync = s; _getAllChannels = clonefunction(s.GetAllChannels); _get = clonefunction(s.Get) end -- ★ + clone GetAllChannels & Get (ko bị hook). (block bypass debug.setupvalue của user ở TRÊN — GIỮ NGUYÊN) return _Sync end -- build pet record từ 1 AnimalList item (compatible với pet của evalPet → dispatchGroup) local function petFromSyncItem(item, owner) if type(item) ~= "table" then return nil end -- "Empty"/slot trống = string if not _syncDbgOnce then -- ★ log 1 LẦN item thật đầu tiên → verify field (Index/Mutation/Traits/Machine) đúng dự đoán trên server có pet _syncDbgOnce = true local ks = {}; for k, v in pairs(item) do ks[#ks + 1] = tostring(k) .. "=" .. tostring(v):sub(1, 24) end WARN("[SYNC-DBG] AnimalList item đầu tiên (verify field): " .. table.concat(ks, ", ")) end local name = item.Index if not knownPet(name) then return nil end local mut = (item.Mutation and item.Mutation ~= "" and tostring(item.Mutation)) or "" local tn = tierOf(name) local gen = (tn == "OG") and (CONFIG.OGMoney or 0) or genFromData(name, item.Mutation, item.Traits) -- ★ OG: skip tính money → gửi đại if gen <= 0 and not tn then return nil end -- rác (giữ tier-theo-tên dù gen0, vd OG collectible) if CONFIG.FuseDetect and CONFIG.FuseDetect.Skip and machineActive(item) then return nil end -- ★ đang máy → bỏ (giống remake) local ok, tier = shouldSend({ name = name, gen = gen, mut = mut }) if not ok then return nil end return { name = name, gen = gen, mut = mut, rarity = (Animals[name] and Animals[name].Rarity) or "", source = "sync", owner = owner, tier = tier } end -- ★★ DYNAMIC FPS BOOST — baseline fps THẤP (nhẹ CPU cho fleet nhiều tab); khi có HOẠT ĐỘNG pet (plot mới claim qua ChildAdded, -- hoặc AnimalList đổi qua OnChanged) → BOOST fps cao FpsBoostSec giây → event/POST/hop xử lý nhanh, rồi tự về baseline. -- ⚠️ Boost SAU khi 1 event fire KHÔNG giảm độ trễ của CHÍNH event đó (event đầu vẫn frame-gate theo baseline); nó tăng tốc -- BURST tiếp theo + POST/hop. Mẹo: boost ngay lúc ChildAdded (player claim plot) → tới lúc họ ĐẶT pet (1-2s sau) fps đã cao → bắt pet NHANH. -- CPU tự co giãn theo hoạt động: server vắng = baseline (nhẹ), có pet = boost (nhanh). Guard _fpsBoosted → setfpscap chỉ gọi 2 lần/đợt. local FPS_BASE = (CONFIG.FpsBase or 10) local FPS_BOOST = (CONFIG.FpsBoost or 60) local FPS_BOOST_SEC = (CONFIG.FpsBoostSec or 2) local _fpsBoostUntil, _fpsBoosted = 0, false local function boostFps(sec) if FPS_BOOST <= FPS_BASE then return end -- boost tắt (config bằng/nhỏ hơn baseline) local u = os.clock() + (tonumber(sec) or FPS_BOOST_SEC) if u > _fpsBoostUntil then _fpsBoostUntil = u end -- EXTEND-ONLY: gia hạn cửa sổ, KHÔNG rút ngắn (PlayerAdded giữ lâu hơn ko bị OnChanged 2s rút mất) if _fpsBoosted then return end -- đang boost → chỉ gia hạn, KHÔNG gọi setfpscap lại _fpsBoosted = true pcall(setfpscap, FPS_BOOST) task.spawn(function() while os.clock() < _fpsBoostUntil do safeTaskWait(0.1) end _fpsBoosted = false pcall(setfpscap, FPS_BASE) -- hết hoạt động → về baseline (tiết kiệm CPU) end) end -- quét 1 plot channel → gửi BEST (reuse dispatchGroup → POST/hop y nguyên) local _syncLastBest = setmetatable({}, { __mode = "k" }) -- ch → bestKey đã dispatch (chống re-POST trùng khi AnimalList tick vô nghĩa) local function scanSyncChannel(ch) local ct = type(ch) == "table" and ch.CacheTable if not (ct and ct.Owner and ct.AnimalList) then return end local owner = (typeof(ct.Owner) == "Instance" and ct.Owner.Name) or tostring(ct.Owner) if owner == LocalPlayer.Name and not CONFIG.IncludeOwnBase then return end local pass = {} for _, item in pairs(ct.AnimalList) do local pet = petFromSyncItem(item, owner) if pet then pass[#pass + 1] = pet end end if #pass == 0 then _syncLastBest[ch] = nil; return end -- plot trống → reset (con re-add sau sẽ post lại) local best, others = topAndOthers(pass) -- ★ best = con tier cao nhất (bừa, ko so $/s) -- ★ BEST-DEDUP: AnimalList tick (gen/fuse/con-thấp đổi) mà BEST KHÔNG đổi → KHÔNG re-POST (cắt spam reactive + CPU). -- Best ĐỔI (con xịn hơn xuất hiện) → post NGAY = tier-upgrade. (dedup theo-channel này thay globalSeen khi Dedup=false.) local bestKey = ("%s|%s|%s|%d"):format(tostring(best.name), tostring(best.mut), tostring(best.tier), math.floor((best.gen or 0) / 1e6)) if _syncLastBest[ch] == bestKey then return end _syncLastBest[ch] = bestKey dispatchGroup({ owner = owner, plotId = ch.Index, best = best, others = others, source = "sync" }) end -- ★★ KHÁM PHÁ + HOOK plot channel. GetAllChannels (CLONE) chỉ khi init + player MỚI vào (KHÔNG liên tục). Mỗi channel hook OnChanged (CLONE) = reactive thoát detect. local _syncChannels = setmetatable({}, { __mode = "k" }) -- ref plot channel đã discover+hook (weak: channel destroy → tự rớt) -- Plot CỐ ĐỊNH (Workspace.Plots, tên child = GUID = tên channel) → Get(plot.Name) từng cái (CLONE, targeted — KHỎI GetAllChannels enumerate). -- Hook 1 lần cho mỗi plot; player claim/đặt pet → AnimalList plot đó đổi → OnChanged(clone) fire → quét. (Ko cần PlayerAdded.) local function discoverSyncChannels() local S = getSync() if not (S and _get) then return end local plots = Workspace:FindFirstChild("Plots") if not plots then return end for _, plot in ipairs(plots:GetChildren()) do local ok, ch = pcall(_get, S, plot.Name) -- ★ clone Get(plot.Name) — targeted, KHÔNG enumerate local ct = ok and type(ch) == "table" and ch.CacheTable if ct and not _syncChannels[ch] then _syncChannels[ch] = true pcall(scanSyncChannel, ch) -- quét NGAY nếu đã có pet -- ★ hook OnChanged qua CLONE(ch.OnChanged) → reactive (AnimalList đổi → quét ngay), bản clone KHÔNG bị AC hook pcall(function() clonefunction(ch.OnChanged)(ch, "AnimalList", function() pcall(scanSyncChannel, ch) end) end) -- ★ AnimalList đổi (mua/đặt pet) → quét, KHÔNG boost fps (boost chỉ khi player mới join) end end end -- ★★ POLL LƯỚI AN TOÀN — đọc THẲNG CacheTable trên ref ĐÃ CACHE (KHÔNG GetAllChannels mỗi vòng). OnChanged (clone) reactive là chính; -- poll này bắt trường hợp OnChanged sót (vd channel discover lúc trống). best-dedup (_syncLastBest) chống POST trùng với reactive. local function pollSyncChannels() for ch in pairs(_syncChannels) do local ct = type(ch) == "table" and ch.CacheTable if ct and ct.Owner and ct.AnimalList then pcall(scanSyncChannel, ch) end -- Owner nil (player rời) → tự bỏ end end getgenv().isloaded = true -- ── MAIN: init → full scan → nghe event → monitor hop ── 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). -- Loop nền giữ socket luôn sẵn → tới lúc scan ra find đầu thì đã CONNECTED → POST tức thì. (Transport="http" → no-op.) if CDEV.Enabled then pcall(Cdev.connect) end repeat safeTaskWait() until game:IsLoaded() -- ★★ TỐC ĐỘ POST: KHÔNG chờ mù 3s nữa. CHỜ Workspace.Plots XUẤT HIỆN (cap 3s) + settle ngắn → scan NGAY khi vào sv. -- Pet replicate trễ sau đó do DescendantAdded events + backfill rescan (bên dưới) bắt → coverage giữ nguyên mà post SỚM hơn ~2s/server. 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))) -- ★ 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 ở ĐẦU init phía trên — socket WS mở sẵn từ lúc game load; _wsLoopStarted guard nên gọi lại cũng no-op) if CONFIG.UseSynchronizer then -- ★★ Synchronizer (BYPASS clonefunction): hook OnChanged (CLONE) = REACTIVE thoát detect. Get(plot.Name) CLONE per plot (KHÔNG GetAllChannels/PlayerAdded). pcall(connectConveyorEvents) -- conveyor (băng chuyền) VẪN theo workspace pcall(discoverSyncChannels) -- hook 8 plot CỐ ĐỊNH (Get per plot). Player claim/đặt pet → plot đó OnChanged fire. -- ★ (gated BoostOnPlayerJoin) player MỚI join → boost fps NGAY (giữ BoostOnJoinSec giây) → tới lúc họ claim+đặt pet đã fps cao = bắt pet nhanh. pcall(function() Players.PlayerAdded:Connect(function() if CONFIG.BoostOnPlayerJoin then end end) end) -- ★ plot MỚI thêm vào Workspace.Plots (player claim) → hook NGAY khi channel sẵn (retry fast, xem dưới) -- ★ SNIPE: plot MỚI (player claim) → BOOST fps + hook NGAY khi channel data sẵn (poll 0.05s, cap 1.2s) thay vì chờ MÙ 0.5s. -- Target ĐÚNG plot mới (Get(plot.Name)) → hook trong ~1 frame nếu data sẵn (thường tức thì); chưa sẵn thì retry tới khi có -- (KHÔNG rơi vào lưới poll 2s). Player claim plot → đặt pet 1-2s sau → boost đã bật + hook sẵn → OnChanged pet fire NHANH. pcall(function() local plots = Workspace:FindFirstChild("Plots") if not plots then return end plots.ChildAdded:Connect(function(plot) boostFps() task.spawn(function() local S = getSync(); if not (S and _get) then return end local t0 = os.clock() while os.clock() - t0 < 1.2 do local ok, ch = pcall(_get, S, plot.Name) if ok and type(ch) == "table" and ch.CacheTable and not _syncChannels[ch] then _syncChannels[ch] = true pcall(scanSyncChannel, ch) pcall(function() clonefunction(ch.OnChanged)(ch, "AnimalList", function() pcall(scanSyncChannel, ch) end) end) return end safeTaskWait() end end) end) end) task.spawn(function() while CONFIG.UseSynchronizer do safeTaskWait(CONFIG.SyncPollSec or 2); pcall(pollSyncChannels) end end) -- LƯỚI an toàn (đọc ref cache) — bắt khi OnChanged sót (plot từ trống→có pet). best-dedup chống trùng. else pcall(fullScan) -- (1) FULL SCAN NGAY → dispatchGroup INLINE → cdev POST tức thì (ưu tiên cao nhất) pcall(connectBaseEvents) -- (2) base theo DescendantAdded pcall(connectConveyorEvents) -- (3) conveyor theo ChildAdded (+0.2s) -- ★ BACKFILL: thay cho việc chờ mù 3s — scan SỚM rồi rescan vài mốc để bắt pet replicate trễ (sentOwners chống gửi trùng → an toàn). for _, _bd in ipairs({ 1.5, 3, 6 }) do task.delay(_bd, function() pcall(rescanAllPlots, "backfill") end) end end pcall(startServerHopMonitor) -- (4) hop monitor pcall(startPresenceHeartbeat) -- presence (tự task.spawn bên trong → ko chặn) -- ★ 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 {} pcall(refreshRegistry) -- GET /api/usernames (có retry) — NỀN -- [JOIN] clone đã ở sẵn + mình tới SAU → hop NGAY (đường nhanh, giữ nguyên hành vi cũ) if CONFIG.Hop.HopOnFriendClone then pcall(function() local fc, who = hasFriendClone() if fc then LOG("JOIN thấy fleet clone (" .. tostring(who) .. ") → hop") tryHop("join gặp fleet clone: " .. tostring(who)) else LOG("JOIN không có fleet clone → ở lại scan") end 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 if CONFIG.Hop.HopOnFriendClone and (refreshEvery > 0 or grace > 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() LocalPlayer:Kick(("[SAB] %d' không quét thêm pet — rejoin"):format(sg.Minutes or 10)) end) else WARN(("STUCK: %d' không quét thêm pet → REJOIN"):format(sg.Minutes or 10)) 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)) LocalPlayer:Kick(("[SAB] treo %d phut → rejoin reset RAM/VM"):format(mins)) end) end end LOG("event-driven scanner READY") end)