diagnostic dragdetector probe universal
--!nonstrict
--[[=========================================================================
DragDetector probe v3
v1 measured movement with no control, so gravity read as success.
v2 added the control but still only measured movement, which cannot tell
"our input never arrived" apart from "the drag was refused".
v3 instruments the drag itself. The detector fires DragStart / DragContinue
/ DragEnd, so we count them. That splits every failure mode apart:
DragStart never fires on OUR input, but fires when YOU drag by hand
-> our injection is the problem (security, aim, coordinate space)
DragStart never fires even when you drag by hand
-> this part is not really draggable; nothing to exploit
DragStart fires but the part does not move
-> the drag is running and being refused or is local-only
DragStart fires and the part moves
-> it works, and we know exactly which method did it
Two things found in the API dump that v1/v2 got wrong:
* VirtualInputManager's mouse methods are RobloxScriptSecurity, so a
plain script cannot call them -- it depends on the executor.
* WorldToViewportPoint excludes the GUI inset. Feeding those coordinates
to a mouse event aims ~36px above the part and misses it. The inset
has to be added back.
=========================================================================]]
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInput = game:GetService("UserInputService")
local GuiService = game:GetService("GuiService")
local LocalPlayer = Players.LocalPlayer
local Mouse = LocalPlayer:GetMouse()
local function guiHost()
if gethui then
local ok, host = pcall(gethui)
if ok and host then return host end
end
local ok, core = pcall(game.GetService, game, "CoreGui")
if ok and core then return core end
return LocalPlayer:WaitForChild("PlayerGui")
end
local gui = Instance.new("ScreenGui")
gui.Name = "DragProbe3"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.Parent = guiHost()
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, 580, 0, 400)
frame.Position = UDim2.new(0, 20, 0, 60)
frame.BackgroundColor3 = Color3.fromRGB(16, 16, 22)
frame.BorderSizePixel = 0
frame.Parent = gui
Instance.new("UICorner", frame).CornerRadius = UDim.new(0, 8)
local text = Instance.new("TextLabel")
text.BackgroundTransparency = 1
text.Position = UDim2.new(0, 12, 0, 10)
text.Size = UDim2.new(1, -24, 1, -20)
text.Font = Enum.Font.Code
text.TextSize = 13
text.TextColor3 = Color3.fromRGB(215, 220, 235)
text.TextXAlignment = Enum.TextXAlignment.Left
text.TextYAlignment = Enum.TextYAlignment.Top
text.TextWrapped = true
text.Parent = frame
local lines = {}
local function log(line)
lines[#lines + 1] = line
if #lines > 25 then table.remove(lines, 1) end
text.Text = table.concat(lines, "\n")
print("[dragprobe] " .. line)
end
local function wait(n)
for _ = 1, n do RunService.Heartbeat:Wait() end
end
task.spawn(function()
log("== DragDetector probe v3 ==")
local det, part
for _, inst in ipairs(workspace:GetDescendants()) do
if inst:IsA("DragDetector") then
local node = inst.Parent
for _ = 1, 3 do
if not node then break end
if node:IsA("BasePart") then det, part = inst, node break end
node = node.Parent
end
end
if det then break end
end
if not det then
log("no DragDetector in workspace. Nothing to test.")
return
end
log("part: " .. part:GetFullName())
log(string.format("Anchored=%s RunLocally=%s Policy=%s",
tostring(part.Anchored), tostring(det.RunLocally),
tostring(det.PermissionPolicy)))
-- THE instrumentation: does the engine think a drag is happening?
local starts, continues, ends = 0, 0, 0
local who = "?"
det.DragStart:Connect(function(player)
starts = starts + 1
who = player and player.Name or "nil"
end)
det.DragContinue:Connect(function() continues = continues + 1 end)
det.DragEnd:Connect(function() ends = ends + 1 end)
local box = Instance.new("SelectionBox")
box.Adornee = part
box.Color3 = Color3.fromRGB(120, 220, 255)
box.Transparency = 0.6
box.LineThickness = 0.04
box.Parent = gui
-- ---------------------------------------------------------------- aim
local inset = GuiService:GetGuiInset()
log(string.format("GUI inset: %d, %d", inset.X, inset.Y))
local cam = workspace.CurrentCamera
local vp, onScreen = cam:WorldToViewportPoint(part.Position)
if not onScreen then
log("part is OFF SCREEN -- look at it and re-run.")
return
end
log(string.format("part at viewport %d,%d -> screen %d,%d",
vp.X, vp.Y, vp.X + inset.X, vp.Y + inset.Y))
-- ---------------------------------------------------------------- vim
local VIM
local okSvc, err = pcall(function()
VIM = game:GetService("VirtualInputManager")
end)
log("VirtualInputManager: " .. (okSvc and "available" or ("BLOCKED " .. tostring(err))))
local function tryMove(x, y, label)
local ok, e = pcall(function() VIM:SendMouseMoveEvent(x, y, game) end)
wait(3)
local loc = UserInput:GetMouseLocation()
local hit = Mouse.Target
log(string.format(" %s -> %s | cursor now %d,%d | over: %s",
label, ok and "sent" or ("ERR " .. tostring(e)),
loc.X, loc.Y, hit and hit.Name or "nothing"))
return ok and hit == part
end
local aimed = false
if VIM then
log("aim test:")
-- the wrong way (what v1/v2 did) and the right way, side by side
aimed = tryMove(vp.X, vp.Y, "viewport coords (no inset)")
local aimed2 = tryMove(vp.X + inset.X, vp.Y + inset.Y, "screen coords (inset added)")
aimed = aimed or aimed2
if aimed2 then
vp = { X = vp.X + inset.X, Y = vp.Y + inset.Y }
end
log(aimed and " -> cursor IS on the part" or
" -> cursor is NOT landing on the part")
end
-- ------------------------------------------------------- injected drag
local function report(tag, from)
local moved = (part.Position - from).Magnitude
log(string.format("%s: starts=%d continue=%d end=%d moved=%.2f by=%s",
tag, starts, continues, ends, moved, who))
end
if VIM and aimed then
starts, continues, ends = 0, 0, 0
local from = part.Position
pcall(function()
VIM:SendMouseButtonEvent(vp.X, vp.Y, 0, true, game, 0)
for i = 1, 20 do
VIM:SendMouseMoveEvent(vp.X, vp.Y - i * 18, game)
RunService.Heartbeat:Wait()
end
VIM:SendMouseButtonEvent(vp.X, vp.Y - 360, 0, false, game, 0)
end)
wait(20)
report("injected drag", from)
end
-- ------------------------------------------------------------ dragframe
starts, continues, ends = 0, 0, 0
local from = part.Position
pcall(function() det.DragFrame = det.DragFrame + Vector3.new(0, 60, 0) end)
wait(25)
report("DragFrame write", from)
-- --------------------------------------------------------- HUMAN CONTROL
log("")
log(">> NOW DRAG THE OUTLINED PART WITH YOUR OWN MOUSE <<")
log(" you have 8 seconds...")
starts, continues, ends = 0, 0, 0
from = part.Position
wait(8 * 60)
report("YOUR manual drag", from)
log("")
if starts == 0 then
log("VERDICT: even YOUR real mouse never started a drag.")
log("This part is not actually draggable for you -- permission policy,")
log("distance, or the game disabled it. No script can fix that.")
else
log("VERDICT: manual dragging DOES work here (" .. starts .. " starts).")
log("So the drag system is live and the problem is our injection --")
log("tell me the aim-test line and I will fix the coordinates.")
end
task.delay(25, function()
box:Destroy()
gui:Destroy()
end)
end)