diagnostic dragdetector probe universal
--!nonstrict
--[[=========================================================================
DragDetector probe v2
v1 was wrong. It measured how far a part moved after each method and
called any movement success -- with no control. An unanchored part drifts
on its own: gravity, settling, the game nudging it. A couple of studs of
that read as "it worked".
v2 fixes the experiment:
* a CONTROL pass first -- do nothing, measure the drift. Every method is
then judged against that baseline, not against zero.
* every push is UPWARD, because gravity opposes it. A part that rises is
unambiguous; a part that moves "somewhat" is not.
* each method runs TWICE and has to work both times.
* the part's own state is reported first, since an anchored part or a
PermissionPolicy of Nobody explains everything on its own.
=========================================================================]]
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local LocalPlayer = Players.LocalPlayer
local SETTLE = 20 -- frames to let things calm down before measuring
local MEASURE = 25 -- frames per measured window
local PUSH = 60 -- studs / screen-pixels-worth of shove
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 = "DragProbe2"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.Parent = guiHost()
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, 560, 0, 360)
frame.Position = UDim2.new(0.5, -280, 0, 40)
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.Text = "probing..."
text.Parent = frame
local lines = {}
local function log(line)
lines[#lines + 1] = line
if #lines > 22 then table.remove(lines, 1) end
text.Text = table.concat(lines, "\n")
print("[dragprobe] " .. line)
end
local function wait(frames)
for _ = 1, frames do RunService.Heartbeat:Wait() end
end
local function findDetector()
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 return inst, node end
node = node.Parent
end
end
end
return nil
end
task.spawn(function()
log("== DragDetector probe v2 (with control) ==")
local det, part = findDetector()
if not det then
log("no DragDetector in workspace -- nothing to test.")
return
end
local function readProp(name)
local ok, v = pcall(function() return det[name] end)
return ok and tostring(v) or "n/a"
end
log("part: " .. part:GetFullName())
log(string.format(" Anchored=%s Enabled=%s RunLocally=%s",
tostring(part.Anchored), readProp("Enabled"), readProp("RunLocally")))
log(string.format(" Response=%s Permission=%s",
readProp("ResponseStyle"), readProp("PermissionPolicy")))
if part.Anchored then
log("!! the part is ANCHORED -- nothing can move it. That is the answer.")
return
end
if readProp("PermissionPolicy") == "Enum.DragDetectorPermissionPolicy.Nobody" then
log("!! PermissionPolicy is Nobody -- the game forbids dragging it.")
return
end
local box = Instance.new("SelectionBox")
box.Adornee = part
box.Color3 = Color3.fromRGB(120, 200, 255)
box.Transparency = 0.6
box.LineThickness = 0.04
box.Parent = gui
-- how much does it move on its own?
local function window(fn)
wait(SETTLE)
local from = part.Position
local ok, err = true, nil
if fn then ok, err = pcall(fn) end
wait(MEASURE)
local to = part.Position
return (to - from).Magnitude, to.Y - from.Y, ok, err
end
log("")
log("control: measuring drift with NO input...")
local baseMag, baseUp = window(nil)
local base2 = select(1, window(nil))
local baseline = math.max(baseMag, base2)
log(string.format(" drifts %.2f / %.2f studs on its own (up %.2f)",
baseMag, base2, baseUp))
log(string.format(" -> anything under %.2f studs means NOTHING", baseline * 3 + 1))
local threshold = baseline * 3 + 1
local results = {}
local function test(name, fn)
local pass, detail = 0, ""
for run = 1, 2 do
local mag, up, ok, err = window(fn)
if not ok then
detail = "ERR " .. tostring(err)
break
end
-- must beat the baseline AND go the way we pushed (up)
if mag > threshold and up > threshold * 0.5 then pass = pass + 1 end
detail = detail .. string.format("[%.1f up%.1f] ", mag, up)
end
results[name] = pass
log(string.format("%s: %s -> %s", name, detail,
pass == 2 and "WORKS" or (pass == 1 and "inconsistent" or "no effect")))
end
-- force the settings once, so later methods get the best chance
pcall(function() det.Enabled = true end)
pcall(function() det.RunLocally = false end)
pcall(function() det.MaxActivationDistance = 1e6 end)
pcall(function()
det.ResponseStyle = Enum.DragDetectorResponseStyle.Geometric
end)
log("")
test("1 DragFrame", function()
det.DragFrame = det.DragFrame + Vector3.new(0, PUSH, 0)
end)
test("2 RestartDrag+Frame", function()
det:RestartDrag()
det.DragFrame = det.DragFrame + Vector3.new(0, PUSH, 0)
end)
local hasVim = pcall(function() return game:GetService("VirtualInputManager") end)
if hasVim then
test("3 simulated drag", function()
local VIM = game:GetService("VirtualInputManager")
local cam = workspace.CurrentCamera
local sp, onScreen = cam:WorldToViewportPoint(part.Position)
if not onScreen then error("off-screen -- look at the part") end
VIM:SendMouseButtonEvent(sp.X, sp.Y, 0, true, game, 0)
for i = 1, 14 do
VIM:SendMouseMoveEvent(sp.X, sp.Y - i * 20, game)
RunService.Heartbeat:Wait()
end
VIM:SendMouseButtonEvent(sp.X, sp.Y - 280, 0, false, game, 0)
end)
else
log("3 simulated drag: no VirtualInputManager on this executor")
end
log("")
local any = false
for name, pass in pairs(results) do
if pass == 2 then any = true end
end
if any then
log("VERDICT: at least one method beat the control, twice, upward.")
log("Tell me which one and the hub will use exactly that.")
else
log("VERDICT: nothing beat the control. Dragging does not work here.")
log("v1 said otherwise because it had no control pass -- a couple of")
log("studs of gravity read as success. It was measuring nothing.")
end
task.delay(20, function()
box:Destroy()
gui:Destroy()
end)
end)