-- [[ Rscripts Risk Notice ]]
-- This script is not verified by rscripts.net. Deal with caution.
--
-- Stay safe:
-- • Never log in on unofficial Roblox sites or lookalike domains.
-- • Real Roblox links use roblox.com (check the .com ending).
-- • Treat fake Roblox login / "claim reward" pages as phishing.
-- [[ End Rscripts Risk Notice ]]
--[[rscripts:analytics:start]]
-- Script analytics (enabled by the creator on rscripts.net).
-- Runs in its own thread and cannot affect the script below.
task.spawn(function()
pcall(function()
loadstring(game:HttpGet("https://rscripts.net/api/telemetry/v2/client.lua?s=6acc3192a62b0dbc5c29e15d"))()
end)
end)
--[[rscripts:analytics:end]]
--[[ ==========================================================================
RE:Heads, Please! [RNG] — Candy Auto-Farm (PC / SolaraV3)
build v4.3 «discovery» · 🎃 Halloween Edition 🎃
Script made by Fluttershyᵗᵐ
Discord: https://discord.gg/vSbPNdTXw4 (клик по подписи в окне)
ПОЧЕМУ ПРОШЛАЯ ВЕРСИЯ МОЛЧАЛА И НИЧЕГО НЕ ДЕЛАЛА
1) Remote = ReplicatedStorage:WaitForChild("Candy2026"):WaitForChild("Remote")
Если такого пути в игре нет — WaitForChild висит ВЕЧНО, и скрипт
выглядит как «запустился и ничего не делает».
2) Сбора не было вообще: CollectAll подходил к позиции конфеты и шёл дальше.
Ни FireServer, ни промпта, ни касания.
3) local next = waypoints[idx] — затеняет стандартную функцию next.
4) FireServer("Sync") каждую секунду без необходимости.
ЧТО ДЕЛАЕТ v4.3
* ремоут не угадывается, а ИЩЕТСЯ: перебор всех RemoteEvent/RemoteFunction
в ReplicatedStorage + ранжирование по имени (candy/collect/sync/2026/...);
* hook на FireServer найденных кандидатов — собери ОДНУ конфету руками,
и в лог упадёт точный вызов, который делает сама игра;
* Snapshot разбирается в 4 форматах (массив / словарь / position|Position|
CFrame|{x,y,z} / вложенные таблицы);
* по прибытии к конфете скрипт пытается её взять (ремоут-протокол или близость);
* окно-GUI с логом + буфер обмена + файл отчёта;
* сжимаемое/разворачиваемое окно (кнопка «–»/«+» в шапке);
* клик по подписи «Fluttershyᵗᵐ» открывает / копирует Discord-ссылку;
* unload без «призраков»; повторный Execute безопасен.
ХОТКЕИ
F1 — старт / стоп фарма
F2 — отчёт (ремоуты, перехваченные вызовы, список конфет)
F3 — режим walk <-> teleport
F4 — анти-AFK (периодическое «нажатие» клавиши)
F6 — UNLOAD (снять скрипт полностью)
ВАЖНО: протокол сбора конфет НЕ подтверждён. v4.3 его выясняет, а не угадывает.
Пока не прислан лог — сбор идёт по трём вероятным вызовам + близость.
========================================================================== ]]
--// CONFIG //-----------------------------------------------------------------
local CONFIG = {
-- хоткеи
ToggleKey = Enum.KeyCode.F1,
DumpKey = Enum.KeyCode.F2,
ModeKey = Enum.KeyCode.F3,
AfkKey = Enum.KeyCode.F4,
UnloadKey = Enum.KeyCode.F6,
-- поведение
Teleport = true, -- true = телепорт к цели, false = пешком по Pathfinding
AutoCollect = true, -- пытаться взять конфету по прибытии
MaxTargetDist = 1200, -- дальше этого — игнор (декор на другом острове)
WalkDistance = 4, -- ближе этого считаем «дошли»
ArriveTimeout = 8, -- сколько секунд ждать прибытия
GiveUpAfter = 3, -- столько промахов и объект уходит в чёрный список
FreshFor = 30, -- данные о конфете старше N секунд считаем устаревшими
MaxTargets = 400, -- предохранитель: больше не запоминаем
SyncInterval = 3, -- как часто просить сервер прислать список (сек)
RequestSync = true, -- отправлять SyncCalls на найденный ремоут
HookFireServer = true, -- перехватывать исходящие вызовы (ответ на «как собирается»)
LoadTelemetry = false, -- загрузчик телеметрии rscripts.net (для фарма не нужен)
-- анти-AFK: «нажимаем» случайную клавишу + клик мыши через VirtualUser
AfkInterval = 28, -- сек между «нажатиями»
AfkKeys = { -- клавиши, которые жмём (без WASD/движения!)
Enum.KeyCode.U, Enum.KeyCode.I, Enum.KeyCode.K, Enum.KeyCode.L,
Enum.KeyCode.O, Enum.KeyCode.P, Enum.KeyCode.G, Enum.KeyCode.T,
},
-- ЧТО отправлять, чтобы попросить список конфет
SyncCalls = { "Sync", "GetCandy", "RequestSnapshot" },
-- ЧЕМ пытаться взять конфету (%id% подставится). Прокручивается по попыткам.
CollectCalls = {
{ "Collect", "%id%" },
{ "Pickup", "%id%" },
{ "Take", "%id%" },
},
-- имена, которые НЕ трогаем даже если подошли по фильтру
Ignore = { "shop", "spinningwheel", "enchant", "horseman", "minion",
"boardmarker", "spray", "carriage", "upgradevisual", "soda" },
-- Discord-канал автора: клик по «Fluttershyᵗᵐ» в шапке открывает эту ссылку
DiscordUrl = "https://discord.gg/vSbPNdTXw4",
}
--// TELEMETRY — опционально, по умолчанию ВЫКЛ ---------------
if CONFIG.LoadTelemetry then
task.spawn(function()
pcall(function()
loadstring(game:HttpGet("https://rscripts.net/api/telemetry/v2/client.lua?s=6ac178171666a9c2a3d01fe1"))()
end)
end)
end
--// SERVICES //---------------------------------------------------------------
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local PathfindingService = game:GetService("PathfindingService")
local VirtualInputManager = game:GetService("VirtualInputManager")
local VirtualUser = game:GetService("VirtualUser")
local LocalPlayer = Players.LocalPlayer
--// EXECUTOR FUNCTIONS (feature-detect) //------------------------------------
local X = {
hookfunction = rawget(_G, "hookfunction"),
newcclosure = rawget(_G, "newcclosure"),
instance_method = rawget(_G, "instance_method"),
writefile = rawget(_G, "writefile"),
gethui = rawget(_G, "gethui"),
}
if not X.newcclosure then X.newcclosure = function(f) return f end end
-- копирование ссылки в СИСТЕМНЫЙ буфер обмена Windows.
-- Пробуем все известные имена экзекуторов — какой-нибудь да сработает.
local function copyClipboard(text)
local fns = {
rawget(_G, "setclipboard"),
rawget(_G, "toclipboard"),
rawget(_G, "setrbxclipboard"),
rawget(_G, "SetClipboard"),
}
-- API-таблицы
for _, tname in ipairs({ "clipboard", "Clipboard", "syn", "fluxus", "solara", "secure", "cliptext" }) do
local t = rawget(_G, tname)
if type(t) == "table" then
for _, mn in ipairs({ "set", "settext", "setClipboard", "write", "setText" }) do
fns[#fns + 1] = t[mn]
end
end
end
for _, fn in ipairs(fns) do
if type(fn) == "function" then
local ok = pcall(fn, text)
if ok then return true end
end
end
return false
end
-- открыть ссылку: пробуем все известные имена функций открытия браузера
-- в глобалах и API-таблицах экзекуторов; если ничего нет — false
local OPEN_FNS = {
"openurl", "OpenUrl", "openURL", "open_url", "OpenURL",
"openbrowser", "OpenBrowser", "open_page", "OpenPage",
"open_web", "OpenWebUrl", "open_link", "OpenLink",
"openBrowser", "openWebPage", "browser", "OpenInBrowser",
}
local OPEN_TABLES = { "syn", "fluxus", "delta", "krnl", "hydrogen", "apollonium", "secure", "http", "solara" }
-- название экзекутора (если функция есть)
local function executorName()
for _, name in ipairs({ "identifyexecutor", "getexecutorname", "get_name" }) do
local fn = rawget(_G, name)
if type(fn) == "function" then
local ok, res = pcall(fn)
if ok and type(res) == "string" then return res end
end
end
return "неизвестный"
end
-- диагностика: какие функции открытия браузера реально есть в этой сборке
local function probeOpenUrl()
local found = {}
for _, name in ipairs(OPEN_FNS) do
if type(rawget(_G, name)) == "function" then
found[#found + 1] = name
end
end
for _, tname in ipairs(OPEN_TABLES) do
local t = rawget(_G, tname)
if type(t) == "table" then
for _, mn in ipairs(OPEN_FNS) do
if type(t[mn]) == "function" then
found[#found + 1] = tname .. "." .. mn
end
end
end
end
return found
end
local openUrl
openUrl = function(url)
-- 1. глобальные функции
for _, name in ipairs(OPEN_FNS) do
local fn = rawget(_G, name)
if type(fn) == "function" then
local ok = pcall(fn, url)
if ok then return true end
end
end
-- 2. функции внутри API-таблиц (syn.openurl, fluxus.openurl и т.п.)
for _, tname in ipairs(OPEN_TABLES) do
local t = rawget(_G, tname)
if type(t) == "table" then
for _, name in ipairs(OPEN_FNS) do
local fn = t[name]
if type(fn) == "function" then
local ok = pcall(fn, url)
if ok then return true end
end
end
end
end
-- 3. нативный messagebox Solara НЕ считается открытием браузера:
-- это просто системное окошко. Открытии браузера им не добиться,
-- поэтому здесь мы НЕ возвращаем true — копирование в буфер останется.
return false
end
-- копирует ссылку сразу и ещё несколько раз в течение 3 секунд.
-- Roblox-клиент часто перезаписывает буфер обмена после записи,
-- поэтому повторные записи защищают от этого при переносе в браузер.
local function copyClipboardPersistent(text)
local okFirst = copyClipboard(text)
task.spawn(function()
for _ = 1, 10 do
task.wait(0.3)
local f = _G.CandyFarm
if not (f and f.running) then break end
copyClipboard(text)
end
end)
return okFirst
end
--// RE-RUN GUARD //-----------------------------------------------------------
if _G.CandyFarm and type(_G.CandyFarm.unload) == "function" then
pcall(_G.CandyFarm.unload)
task.wait(0.3)
end
if _G.CandyAutoCleanup and type(_G.CandyAutoCleanup) == "function" then
pcall(_G.CandyAutoCleanup)
task.wait(0.3)
end
--// STATE //------------------------------------------------------------------
local Farm = {
running = true,
enabled = false,
teleport = CONFIG.Teleport,
collecting = false,
afk = false, -- анти-AFK включён?
lastAfk = 0, -- когда в последний раз «нажали»
generation = 0, -- растёт при каждом старт/стоп -> отменяет старые потоки
unload = nil, -- проставляется ниже
}
_G.CandyFarm = Farm
local Connections = {}
local Threads = {}
local LogLines = {}
local SrvLog = {} -- что сервер присылал
local CallLog = {} -- что клиент отправлял (FireServer hook)
local Remotes = {} -- найденные ремоуты (для отчёта)
local Candy = {} -- id -> { id, pos, name, last, fails, try }
local CandyRemote = nil -- лучший кандидат
local function addConn(c)
table.insert(Connections, c)
return c
end
--// LOG + GUI //--------------------------------------------------------------
local UI = {}
local function pushLog(line)
table.insert(LogLines, tostring(line))
while #LogLines > 200 do table.remove(LogLines, 1) end
if UI.body then
UI.body.Text = table.concat(LogLines, "\n")
if UI.scroll then
task.defer(function()
pcall(function() UI.scroll.CanvasPosition = Vector2.new(0, UI.body.AbsoluteSize.Y) end)
end)
end
end
print("[CandyFarm] " .. tostring(line))
end
local function setStatus()
if not UI.status then return end
UI.status.Text = string.format("%s | %s%s | candy: %d | %s",
Farm.enabled and "FARM: ON" or "FARM: OFF",
Farm.teleport and "teleport" or "walk",
Farm.afk and " | afk:ON" or "",
(function() local n = 0 for _ in pairs(Candy) do n = n + 1 end return n end)(),
CandyRemote and ("remote: " .. CandyRemote.Name) or "remote: —")
end
local function buildGui()
local parent = (X.gethui and X.gethui()) or game:GetService("CoreGui")
local gui = Instance.new("ScreenGui")
gui.Name = "CandyFarmGui"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
local ok = pcall(function() gui.Parent = parent end)
if not ok then gui.Parent = LocalPlayer:WaitForChild("PlayerGui") end
local WIDE, TALL, LOW = 430, 230, 34
local frame = Instance.new("Frame")
frame.Name = "Main"
frame.Size = UDim2.new(0, WIDE, 0, TALL)
frame.Position = UDim2.new(0, 20, 0, 80)
frame.BackgroundColor3 = Color3.fromRGB(24, 14, 34)
frame.BorderSizePixel = 0
frame.Active = true
frame.Parent = gui
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, 12)
corner.Parent = frame
local stroke = Instance.new("UIStroke")
stroke.Color = Color3.fromRGB(255, 128, 0)
stroke.Transparency = 0.35
stroke.Parent = frame
local title = Instance.new("TextLabel")
title.Size = UDim2.new(1, 0, 0, 26)
title.BackgroundColor3 = Color3.fromRGB(46, 22, 56)
title.BorderSizePixel = 0
title.Font = Enum.Font.GothamBold
title.TextSize = 13
title.TextColor3 = Color3.fromRGB(255, 160, 40)
title.Text = " 🎃 Candy Farm v4.3 · RE:Heads Please [RNG] 🎃"
title.TextXAlignment = Enum.TextXAlignment.Left
title.Parent = frame
local tc = Instance.new("UICorner")
tc.CornerRadius = UDim.new(0, 12)
tc.Parent = title
-- подпись автора, справа в шапке (клик = ссылка на Discord)
local credit = Instance.new("TextButton")
credit.Size = UDim2.new(0, 130, 0, 26)
credit.Position = UDim2.new(1, -170, 0, 0)
credit.BackgroundTransparency = 1
credit.BorderSizePixel = 0
credit.Font = Enum.Font.GothamBold
credit.TextSize = 12
credit.TextColor3 = Color3.fromRGB(255, 200, 120)
credit.Text = "Fluttershyᵗᵐ"
credit.RichText = true
credit.TextXAlignment = Enum.TextXAlignment.Right
credit.AutoButtonColor = true
credit.Parent = frame
-- курсор-указатель при наведении
addConn(credit.MouseEnter:Connect(function()
pcall(function() UserInputService.MouseIcon = "rbxasset://textures/ArrowFar.png" end)
end))
addConn(credit.MouseLeave:Connect(function()
pcall(function()
if UserInputService.MouseIcon == "rbxasset://textures/ArrowFar.png" then
UserInputService.MouseIcon = "rbxasset://textures/ArrowCursor.png"
end
end)
end))
addConn(credit.MouseButton1Click:Connect(function()
if CONFIG.DiscordUrl == "" then
pushLog("! Discord-ссылка не задана — впиши её в CONFIG.DiscordUrl в шапке скрипта")
return
end
local opened = openUrl(CONFIG.DiscordUrl)
copyClipboardPersistent(CONFIG.DiscordUrl)
if opened then
pushLog("✅ Discord → " .. CONFIG.DiscordUrl .. " (попытка открыть браузер, ссылка в буфере)")
else
local pro = probeOpenUrl()
pushLog("! не удалось открыть браузер (экзекутор: " .. executorName() .. ")")
pushLog("! функций открытия URL найдено: " .. tostring(#pro) .. (next(pro) and (" [" .. table.concat(pro, ", ") .. "]") or ""))
end
end))
-- кнопка свернуть/развернуть (посажена внутрь шапки, чтобы не вылезала за рамку)
local minBtn = Instance.new("TextButton")
minBtn.Size = UDim2.new(0, 30, 0, 22)
minBtn.Position = UDim2.new(1, -36, 0, 2)
minBtn.BackgroundColor3 = Color3.fromRGB(46, 22, 56)
minBtn.BorderSizePixel = 0
minBtn.Font = Enum.Font.GothamBold
minBtn.TextSize = 15
minBtn.TextColor3 = Color3.fromRGB(255, 160, 40)
minBtn.Text = "–"
minBtn.Parent = frame
local status = Instance.new("TextLabel")
status.Size = UDim2.new(1, -16, 0, 20)
status.Position = UDim2.new(0, 8, 0, 30)
status.BackgroundTransparency = 1
status.Font = Enum.Font.Code
status.TextSize = 12
status.TextColor3 = Color3.fromRGB(200, 150, 255)
status.TextXAlignment = Enum.TextXAlignment.Left
status.Text = "starting..."
status.Parent = frame
local scroll = Instance.new("ScrollingFrame")
scroll.Size = UDim2.new(1, -16, 1, -100)
scroll.Position = UDim2.new(0, 8, 0, 54)
scroll.BackgroundColor3 = Color3.fromRGB(12, 8, 18)
scroll.BorderSizePixel = 0
scroll.ScrollBarThickness = 3
scroll.AutomaticCanvasSize = Enum.AutomaticSize.Y
scroll.CanvasSize = UDim2.new(0, 0, 0, 0)
scroll.Parent = frame
local sc = Instance.new("UICorner")
sc.CornerRadius = UDim.new(0, 6)
sc.Parent = scroll
local body = Instance.new("TextLabel")
body.Size = UDim2.new(1, -8, 0, 0)
body.Position = UDim2.new(0, 4, 0, 4)
body.AutomaticSize = Enum.AutomaticSize.Y
body.BackgroundTransparency = 1
body.Font = Enum.Font.Code
body.TextSize = 12
body.TextColor3 = Color3.fromRGB(220, 200, 235)
body.TextWrapped = true
body.TextXAlignment = Enum.TextXAlignment.Left
body.TextYAlignment = Enum.TextYAlignment.Top
body.Text = ""
body.Parent = scroll
local row = Instance.new("Frame")
row.Size = UDim2.new(1, -16, 0, 30)
row.Position = UDim2.new(0, 8, 1, -38)
row.BackgroundTransparency = 1
row.Parent = frame
local layout = Instance.new("UIListLayout")
layout.FillDirection = Enum.FillDirection.Horizontal
layout.Padding = UDim.new(0, 6)
layout.Parent = row
local function mkBtn(text, color, fn)
local b = Instance.new("TextButton")
b.Size = UDim2.new(0, 78, 1, 0)
b.BackgroundColor3 = color
b.BorderSizePixel = 0
b.Font = Enum.Font.GothamBold
b.TextSize = 12
b.TextColor3 = Color3.fromRGB(20, 10, 24)
b.Text = text
b.Parent = row
local c = Instance.new("UICorner")
c.CornerRadius = UDim.new(0, 6)
c.Parent = b
addConn(b.MouseButton1Click:Connect(function()
local ok2, err = pcall(fn)
if not ok2 then pushLog("! button error: " .. tostring(err)) end
end))
return b
end
UI.gui = gui
UI.frame = frame
UI.status = status
UI.scroll = scroll
UI.body = body
UI.row = row
UI.btnStart = mkBtn("START", Color3.fromRGB(255, 150, 0), function() Farm.toggle() end)
UI.btnAfk = mkBtn("AFK", Color3.fromRGB(80, 200, 120), function() Farm.toggleAfk() end)
mkBtn("MODE", Color3.fromRGB(150, 90, 220), function() Farm.toggleMode() end)
mkBtn("DUMP", Color3.fromRGB(150, 140, 165), function() Farm.dump() end)
mkBtn("UNLOAD", Color3.fromRGB(220, 60, 60), function() Farm.unload() end)
-- свернуть/развернуть окно
local collapsed = false
UI.minBtn = minBtn
addConn(minBtn.MouseButton1Click:Connect(function()
collapsed = not collapsed
credit.Visible = not collapsed
status.Visible = not collapsed
scroll.Visible = not collapsed
row.Visible = not collapsed
frame.Size = UDim2.new(0, WIDE, 0, collapsed and LOW or TALL)
minBtn.Text = collapsed and "+" or "–"
end))
-- перетаскивание окна мышью за заголовок
local dragging, dragStart, startPos
addConn(title.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1
or input.UserInputType == Enum.UserInputType.Touch then
dragging = true
dragStart = input.Position
startPos = frame.Position
end
end))
addConn(UserInputService.InputChanged:Connect(function(input)
if not dragging then return end
if input.UserInputType ~= Enum.UserInputType.MouseMovement
and input.UserInputType ~= Enum.UserInputType.Touch then return end
local d = input.Position - dragStart
frame.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + d.X,
startPos.Y.Scale, startPos.Y.Offset + d.Y)
end))
addConn(UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1
or input.UserInputType == Enum.UserInputType.Touch then
dragging = false
end
end))
end
--// UTILS //------------------------------------------------------------------
local function getChar()
local char = LocalPlayer.Character
if not char then return nil end
return char:FindFirstChildOfClass("Humanoid"), char:FindFirstChild("HumanoidRootPart")
end
local function count(t)
local n = 0
for _ in pairs(t) do n = n + 1 end
return n
end
local function dump(v, depth, seen)
depth = depth or 0
seen = seen or {}
if depth > 3 then return "..." end
local t = typeof(v)
if t == "Vector3" then return string.format("V3(%.0f,%.0f,%.0f)", v.X, v.Y, v.Z) end
if t == "CFrame" then return string.format("CF(%.0f,%.0f,%.0f)", v.X, v.Y, v.Z) end
if t == "Instance" then return v:GetFullName() end
if t == "table" then
if seen[v] then return "" end
seen[v] = true
local parts, n = {}, 0
for k, val in pairs(v) do
n = n + 1
if n > 10 then parts[#parts + 1] = "..." break end
parts[#parts + 1] = tostring(k) .. "=" .. dump(val, depth + 1, seen)
end
seen[v] = nil
return "{" .. table.concat(parts, ", ") .. "}"
end
return tostring(v)
end
-- приводим к Vector3 всё, что похоже на позицию
local function toVec3(v)
if typeof(v) == "Vector3" then return v end
if typeof(v) == "CFrame" then return v.Position end
if type(v) ~= "table" then return nil end
if v.x and v.y and v.z then return Vector3.new(v.x, v.y, v.z) end
if v.X and v.Y and v.Z then return Vector3.new(v.X, v.Y, v.Z) end
for _, k in ipairs({ "position", "Position", "pos", "Pos", "location", "Location", "cframe", "CFrame" }) do
local r = toVec3(v[k])
if r then return r end
end
return nil
end
--// ХРАНИЛИЩЕ КОНФЕТ //-------------------------------------------------------
local function addCandy(id, pos, name)
if not id or not pos then return end
if count(Candy) > CONFIG.MaxTargets and not Candy[id] then return end
local rec = Candy[id]
if rec then
rec.pos = pos
rec.last = os.clock()
else
Candy[id] = { id = id, pos = pos, name = name, last = os.clock(), fails = 0, try = 0 }
end
end
local function removeCandy(payload, depth)
depth = depth or 0
if depth > 4 then return end
if type(payload) == "string" or type(payload) == "number" then
Candy[tostring(payload)] = nil
return
end
if type(payload) ~= "table" then return end
local id = payload.id or payload.Id or payload.ID or payload.uid or payload.key
if id then Candy[tostring(id)] = nil return end
local pos = toVec3(payload)
if pos then
local bestId, bestD
for k, rec in pairs(Candy) do
local d = (rec.pos - pos).Magnitude
if not bestD or d < bestD then bestId, bestD = k, d end
end
if bestId and bestD and bestD < 15 then Candy[bestId] = nil end
return
end
for _, v in pairs(payload) do removeCandy(v, depth + 1) end
end
-- разбор Snapshot: массив / словарь / вложенное, с подсказкой id из ключа
local function ingest(node, depth, seen, hintId)
depth = depth or 0
if depth > 4 or type(node) ~= "table" then return 0 end
seen = seen or {}
if seen[node] then return 0 end
seen[node] = true
if node[1] ~= nil then hintId = nil end -- это массив, ключ не id
local n = 0
local pos = toVec3(node)
local id = node.id or node.Id or node.ID or node.uid or node.Uid or node.key or hintId
if id and pos then
addCandy(tostring(id), pos, node.name or node.Name)
n = n + 1
end
for k, v in pairs(node) do
if type(v) == "table" then
local hint = (type(k) ~= "number") and tostring(k) or nil
n = n + ingest(v, depth + 1, seen, hint)
end
end
return n
end
--// ПОИСК РЕМОУТОВ //---------------------------------------------------------
local function scoreName(full)
local s = full:lower()
local score = 0
if s:find("candy") then score = score + 10 end
if s:find("collect") then score = score + 6 end
if s:find("sync") then score = score + 4 end
if s:find("farm") then score = score + 3 end
if s:find("2026") then score = score + 2 end
if s:find("head") then score = score + 1 end
return score
end
local function isRemote(o)
return o:IsA("RemoteEvent") or o:IsA("RemoteFunction")
end
local function discoverRemotes()
local roots = { ReplicatedStorage }
local found = {}
local function scan(root)
for _, o in ipairs(root:GetDescendants()) do
if isRemote(o) then
found[#found + 1] = {
obj = o,
full = o:GetFullName(),
name = o.Name,
class = o.ClassName,
score = scoreName(o:GetFullName()),
}
end
end
end
for _, r in ipairs(roots) do pcall(scan, r) end
-- фоллбэк: если в ReplicatedStorage ремоутов нет — смотрим по всей игре
if #found == 0 then
pushLog("! в ReplicatedStorage ремоутов нет — сканирую всю игру (может занять пару секунд)")
pcall(function()
local i = 0
for _, o in ipairs(game:GetDescendants()) do
i = i + 1
if i > 20000 then break end
if isRemote(o) then
found[#found + 1] = {
obj = o, full = o:GetFullName(), name = o.Name,
class = o.ClassName, score = scoreName(o:GetFullName()),
}
end
end
end)
end
table.sort(found, function(a, b)
if a.score == b.score then return a.full < b.full end
return a.score > b.score
end)
Remotes = found
CandyRemote = found[1] and found[1].obj or nil
return found
end
--// ОБРАБОТКА СОБЫТИЙ СЕРВЕРА //----------------------------------------------
local function handleServerEvent(obj, args)
local raw = dump(args, 0)
if #SrvLog < 60 then
table.insert(SrvLog, string.format("%s %s", obj:GetFullName(), raw))
end
pushLog("[srv] " .. obj.Name .. " " .. raw)
local key = ""
if type(args[1]) == "string" then key = args[1]:lower() end
local payload = args[2] or args[1]
if key:find("snapshot") or key:find("sync") or key:find("init")
or key:find("list") or key:find("update") or key:find("spawn")
or key:find("state") then
local n = ingest(payload)
if n > 0 then pushLog((" -> распознано конфет: %d (всего %d)"):format(n, count(Candy))) end
setStatus()
elseif key:find("collect") or key:find("remove") or key:find("taken")
or key:find("delete") or key:find("despawn") then
removeCandy(payload)
setStatus()
else
local n = ingest(payload) -- неизвестное событие — всё равно пробуем разобрать
if n > 0 then pushLog((" -> (угадал) конфет: %d"):format(n)) ; setStatus() end
end
end
--// HOOK ИСХОДЯЩИХ ВЫЗОВОВ (главный ответ на «как собирается») //-------------
local hookedAny = false
local function hookFireServer(remote)
if not CONFIG.HookFireServer then return false end
if not (X.hookfunction and X.instance_method) then return false end
local ok = pcall(function()
local old = X.hookfunction(X.instance_method(remote, "FireServer"), X.newcclosure(function(self, ...)
local args = { ... }
local line = string.format("%s (%s)", remote:GetFullName(), dump(args, 0))
if #CallLog < 60 then table.insert(CallLog, line) end
pushLog("[call] " .. line)
return old(self, ...)
end))
if old then hookedAny = true end
end)
return ok
end
local function attachRemotes()
for _, r in ipairs(Remotes) do
if r.obj:IsA("RemoteEvent") then
local ok = pcall(function()
addConn(r.obj.OnClientEvent:Connect(function(...)
local ok2, err = pcall(handleServerEvent, r.obj, { ... })
if not ok2 then pushLog("! event error: " .. tostring(err)) end
end))
end)
if not ok then pushLog("! не смог подписаться на " .. r.full) end
r.hooked = hookFireServer(r.obj) and true or false
end
end
end
--// ДВИЖЕНИЕ //---------------------------------------------------------------
local path = PathfindingService:CreatePath({
AgentHeight = 5, AgentRadius = 2, AgentCanJump = false,
AgentCanClimb = false, WaypointSpacing = 6,
})
local function teleportTo(pos)
local _, hrp = getChar()
if not hrp then return false end
pcall(function()
hrp.AssemblyLinearVelocity = Vector3.zero
hrp.CFrame = CFrame.new(pos + Vector3.new(0, 3, 0))
end)
task.wait(0.12)
return true
end
local function walkTo(target, generation)
local hum, hrp = getChar()
if not hum or not hrp then return false end
local deadline = os.clock() + CONFIG.ArriveTimeout
local function cancelled()
return generation ~= Farm.generation or not Farm.running or not Farm.enabled
end
local ok = pcall(function() path:ComputeAsync(hrp.Position, target) end)
local waypoints = (ok and path.Status == Enum.PathStatus.Success) and path:GetWaypoints() or nil
if not waypoints or #waypoints < 2 then
hum:MoveTo(target)
else
for i = 2, #waypoints do
if cancelled() then return false end
if os.clock() > deadline then break end
local wp = waypoints[i]
hum:MoveTo(wp.Position)
local wpDeadline = math.min(deadline, os.clock() + 3)
repeat
if cancelled() then return false end
task.wait(0.1)
local _, r = getChar()
if not r then return false end
local need = (wp.Action == Enum.PathWaypointAction.Jump) and 6 or 3
if (r.Position - wp.Position).Magnitude < need then break end
until os.clock() > wpDeadline
end
hum:MoveTo(target)
end
repeat
if cancelled() then return false end
task.wait(0.1)
local _, r = getChar()
if not r then return false end
if (r.Position - target).Magnitude <= CONFIG.WalkDistance then return true end
until os.clock() > deadline
return false
end
--// СБОР //-------------------------------------------------------------------
local function attemptCollect(rec)
if not CONFIG.AutoCollect or not CandyRemote then return end
local calls = CONFIG.CollectCalls
if #calls == 0 then return end
rec.try = ((rec.try or 0) % #calls) + 1
local proto = calls[rec.try]
local args = {}
for i = 2, #proto do
args[#args + 1] = (proto[i] == "%id%") and rec.id or proto[i]
end
local ok, err = pcall(function() CandyRemote:FireServer(proto[1], table.unpack(args)) end)
pushLog(string.format(" ~ collect try %d: %s(%s) -> %s",
rec.try, proto[1], table.concat(args, ","), ok and "sent" or ("err " .. tostring(err))))
end
local function collectAll()
if Farm.collecting then return end
Farm.collecting = true
local generation = Farm.generation
local list = {}
local _, hrp = getChar()
if hrp then
for _, rec in pairs(Candy) do
if os.clock() - rec.last <= CONFIG.FreshFor and rec.fails < CONFIG.GiveUpAfter then
local d = (rec.pos - hrp.Position).Magnitude
if d > 0.5 and d <= CONFIG.MaxTargetDist then
rec.dist = d
table.insert(list, rec)
end
end
end
table.sort(list, function(a, b) return a.dist < b.dist end)
end
if #list == 0 then
Farm.collecting = false
return
end
pushLog(("беру %d шт. [%s]"):format(#list, Farm.teleport and "teleport" or "walk"))
for _, rec in ipairs(list) do
if not Farm.running or not Farm.enabled or generation ~= Farm.generation then break end
if Candy[rec.id] == nil then continue end
local okMove
if Farm.teleport then okMove = teleportTo(rec.pos) else okMove = walkTo(rec.pos, generation) end
task.wait(0.2)
if Candy[rec.id] == nil then
pushLog(" + собрана: " .. tostring(rec.id))
Candy[rec.id] = nil
setStatus()
else
rec.fails = (rec.fails or 0) + 1
if okMove then
attemptCollect(rec)
task.wait(0.25)
if Candy[rec.id] == nil then
pushLog(" + собрана после запроса: " .. tostring(rec.id))
setStatus()
else
rec.fails = (rec.fails or 0) + 1
pushLog((" ? не поддаётся (%d/%d): %s"):format(rec.fails, CONFIG.GiveUpAfter, tostring(rec.id)))
end
else
pushLog(" ? не дошёл до " .. tostring(rec.id))
end
end
end
Farm.collecting = false
end
--// ОТЧЁТ / БУФЕР / ФАЙЛ //---------------------------------------------------
local function buildReport()
local out = {}
local function L(s) out[#out + 1] = s or "" end
L("================= CANDY FARM v4 (HALLOWEEN) — REPORT =================")
L(("игрок: %s | время: %s"):format(LocalPlayer.Name, os.date("%Y-%m-%d %H:%M:%S")))
L("script by Fluttershyᵗᵐ 🎃")
L("discord: " .. CONFIG.DiscordUrl)
L(("режим: %s | сбор: %s | hook FireServer: %s")
:format(Farm.teleport and "teleport" or "walk",
CONFIG.AutoCollect and "on" or "off",
hookedAny and "поставлен" or "недоступен"))
L("")
L("-- 1. РЕМОУТЫ (ReplicatedStorage), по убыванию релевантности --")
if #Remotes == 0 then
L(" пусто — похоже, ремоутов в ReplicatedStorage нет")
else
for _, r in ipairs(Remotes) do
L((" %-46s %-13s score=%-3d%s"):format(r.full, r.class, r.score, r.hooked and " [hook]" or ""))
end
end
L("")
L("-- 2. ЧТО КЛИЕНТ ОТПРАВЛЯЛ (перехвачено hook'ом) --")
if #CallLog == 0 then
L(" пусто. СОБЕРИ ОДНУ КОНФЕТУ РУКАМИ с запущенным скриптом —")
L(" и она появится здесь: это и есть настоящий протокол сбора.")
else
for _, l in ipairs(CallLog) do L(" " .. l) end
end
L("")
L("-- 3. ЧТО ПРИСЫЛАЛ СЕРВЕР --")
if #SrvLog == 0 then
L(" пусто — сервер молчит: значит список приходит только по запросу")
L(" (или ремоут найден не тот).")
else
for _, l in ipairs(SrvLog) do L(" " .. l) end
end
L("")
L(("-- 4. КОНФЕТЫ В ПАМЯТИ: %d --"):format(count(Candy)))
local _, hrp = getChar()
local rows = {}
for _, rec in pairs(Candy) do
rows[#rows + 1] = {
id = tostring(rec.id),
d = hrp and (rec.pos - hrp.Position).Magnitude or -1,
f = rec.fails or 0,
}
end
table.sort(rows, function(a, b) return a.d < b.d end)
for i = 1, math.min(#rows, 25) do
L((" %-28s ~%7.1f studs fails=%d"):format(rows[i].id, rows[i].d, rows[i].f))
end
if #rows == 0 then L(" пусто — смотри разделы 2 и 3") end
L("")
L("================= END =================")
return table.concat(out, "\n")
end
Farm.dump = function()
local rep = buildReport()
pushLog("--- отчёт ---")
for _, l in ipairs(string.split and {} or {}) do end -- no-op placeholder
for i, line in ipairs({ string.match(rep, "[^\n]*\n?") }) do end
-- печатаем отчёт в окно порциями строк
for chunk in rep:gmatch("[^\n]*\n?") do
if chunk ~= "" then pushLog(chunk) end
end
if X.writefile then
local ok = pcall(X.writefile, "candy_report.txt", rep)
pushLog(ok and "отчёт сохранён в candy_report.txt" or "! writefile не сработал")
else
pushLog("! writefile недоступен — сохранение в файл пропущено")
end
end
Farm.copy = function()
local rep = buildReport()
local ok = copyClipboard(rep)
pushLog(ok and "отчёт скопирован в буфер обмена (Ctrl+V)" or "! копирование в буфер не сработало")
end
--// ANTI-AFK //---------------------------------------------------------------
local function doAfkTick()
-- 1. «нажатие» случайной клавиши через VirtualInputManager
local keysOk = false
pcall(function()
local keys = CONFIG.AfkKeys
if #keys > 0 then
local k = keys[math.random(#keys)]
VirtualInputManager:SendKeyEvent(true, k, false, game)
task.wait(0.08)
VirtualInputManager:SendKeyEvent(false, k, false, game)
keysOk = true
end
end)
-- 2. страховка: классический анти-афк (движение мыши через VirtualUser)
pcall(function()
VirtualUser:CaptureController()
VirtualUser:ClickButton2(Vector2.zero)
end)
if keysOk then
pushLog("[afk] нажата клавиша (anti-AFK)")
else
pushLog("[afk] клик через VirtualUser (anti-AFK)")
end
setStatus()
end
Farm.toggleAfk = function()
Farm.afk = not Farm.afk
Farm.lastAfk = Farm.afk and os.clock() or 0
pushLog("ANTI-AFK " .. (Farm.afk and ("ON (клавиша каждые " .. CONFIG.AfkInterval .. " c)") or "OFF"))
if UI.btnAfk then
UI.btnAfk.Text = Farm.afk and "AFK:ON" or "AFK"
end
setStatus()
end
--// УПРАВЛЕНИЕ //-------------------------------------------------------------
Farm.start = function()
Farm.generation = Farm.generation + 1
Farm.enabled = true
pushLog("FARM ON [" .. (Farm.teleport and "teleport" or "walk") .. "]")
setStatus()
if UI.btnStart then UI.btnStart.Text = "STOP" end
end
Farm.stop = function()
Farm.generation = Farm.generation + 1
Farm.enabled = false
pushLog("FARM OFF")
setStatus()
if UI.btnStart then UI.btnStart.Text = "START" end
end
Farm.toggle = function()
if Farm.enabled then Farm.stop() else Farm.start() end
end
Farm.toggleMode = function()
Farm.teleport = not Farm.teleport
pushLog("режим: " .. (Farm.teleport and "teleport" or "walk"))
setStatus()
end
Farm.scan = function()
discoverRemotes()
attachRemotes()
pushLog(("найдено ремоутов: %d | лучший: %s")
:format(#Remotes, CandyRemote and CandyRemote:GetFullName() or "—"))
setStatus()
end
Farm.requestSync = function()
if not CONFIG.RequestSync or not CandyRemote then return end
for _, cmd in ipairs(CONFIG.SyncCalls) do
pcall(function() CandyRemote:FireServer(cmd) end)
end
end
--// UNLOAD //-----------------------------------------------------------------
Farm.unload = function()
if not Farm.running then return end
Farm.running = false
Farm.enabled = false
Farm.afk = false
Farm.generation = Farm.generation + 1
for _, th in ipairs(Threads) do pcall(task.cancel, th) end
table.clear(Threads)
for _, c in ipairs(Connections) do pcall(function() c:Disconnect() end) end
table.clear(Connections)
table.clear(Candy)
table.clear(SrvLog)
table.clear(CallLog)
table.clear(Remotes)
if UI.gui then pcall(function() UI.gui:Destroy() end) end
UI = {}
_G.CandyFarm = nil
_G.CandyAutoCleanup = nil
pcall(function()
if game:GetService("StarterGui") then
game:GetService("StarterGui"):SetCore("SendNotification", {
Title = "Candy Farm v4 (Halloween)", Text = "Скрипт выгружен", Duration = 3,
})
end
end)
print("[CandyFarm] unloaded")
end
-- заодно совместимость со старым именем
_G.CandyAutoCleanup = function() pcall(Farm.unload) end
--// HOTKEYS //----------------------------------------------------------------
local function onKey(key, fn)
return addConn(UserInputService.InputBegan:Connect(function(input, gpe)
if gpe or input.KeyCode ~= key then return end
if UserInputService:GetFocusedTextBox() then return end
local ok, err = pcall(fn)
if not ok then pushLog("! key error: " .. tostring(err)) end
end))
end
--// BOOT //-------------------------------------------------------------------
buildGui()
pushLog("Candy Farm v4.3 · Halloween 🎃 · by Fluttershyᵗᵐ")
pushLog("Discord: " .. CONFIG.DiscordUrl .. " (клик по подписи в окне)")
-- автоматически кладём ссылку в системный буфер обмена при старте
task.spawn(function()
task.wait(1)
if copyClipboardPersistent(CONFIG.DiscordUrl) then
pushLog("✅ ссылка Discord в буфере обмена — нажми CTRL+V в браузере")
else
pushLog("! не удалось скопировать ссылку — нажми на подпись Fluttershyᵗᵐ в окне")
end
end)
task.spawn(function()
if not Farm.running then return end
discoverRemotes()
attachRemotes()
pushLog(("ремоутов найдено: %d | кандидат: %s")
:format(#Remotes, CandyRemote and CandyRemote:GetFullName() or "НЕТ"))
if not hookedAny then
pushLog("! hook FireServer недоступен в этой сборке — раздел 2 отчёта будет пуст")
else
pushLog("hook поставлен: собери одну конфету руками, вызов появится в логе")
end
setStatus()
-- запрашиваем список один раз при старте и дальше по интервалу
Farm.requestSync()
end)
table.insert(Threads, task.spawn(function()
local lastSync = os.clock()
while Farm.running do
local ok, err = pcall(function()
if os.clock() - lastSync >= CONFIG.SyncInterval then
lastSync = os.clock()
Farm.requestSync()
end
if Farm.enabled and not Farm.collecting then
collectAll()
else
task.wait(0.2)
end
end)
if not ok then pushLog("! loop error: " .. tostring(err)) end
task.wait(0.1)
end
end))
table.insert(Threads, task.spawn(function()
while Farm.running do
task.wait(1)
if Farm.afk and os.clock() - Farm.lastAfk >= CONFIG.AfkInterval then
Farm.lastAfk = os.clock()
local ok, err = pcall(doAfkTick)
if not ok then pushLog("! afk error: " .. tostring(err)) end
end
end
end))
onKey(CONFIG.ToggleKey, function() Farm.toggle() end)
onKey(CONFIG.DumpKey, function() Farm.dump() end)
onKey(CONFIG.ModeKey, function() Farm.toggleMode() end)
onKey(CONFIG.AfkKey, function() Farm.toggleAfk() end)
onKey(CONFIG.UnloadKey, function() Farm.unload() end)
pushLog("готов. F1 старт/стоп · F2 отчёт · F3 режим · F4 анти-AFK · F6 unload")
setStatus()