diagnostic network-ownership probe universal
--!nonstrict
--[[=========================================================================
network ownership probe
One part. One question: when this client writes Velocity to a nearby
unanchored part, does the SERVER accept it?
Nothing here is shared with the Optimizer -- no library, no registry, no
modes -- so whatever it reports is about your executor and this game, not
about that script.
It runs four measurements:
1. capabilities what the executor actually exposes
2. raw push velocity only, no ownership tricks at all
3. boosted push same, after ReplicationFocus + SimulationRadius
4. release stop pushing and see if the part snaps back
(4) is the important one. A part that moves while pushed and then returns
to where it started was never yours: you were watching local prediction,
and the server's position was the real one the whole time.
=========================================================================]]
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local LocalPlayer = Players.LocalPlayer
local PUSH_SECONDS = 4
local PUSH_SPEED = 60 -- studs/sec, straight up: unmistakable
local SEARCH_RANGE = 200
--=========================================================================
-- readout
--=========================================================================
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 = "OwnershipProbe"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.Parent = guiHost()
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, 460, 0, 240)
frame.Position = UDim2.new(0.5, -230, 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
text.Text = table.concat(lines, "\n")
print("[probe] " .. line)
end
--=========================================================================
-- helpers
--=========================================================================
local function isCharacterPart(part)
local node = part
for _ = 1, 4 do
node = node.Parent
if not node then return false end
if node:FindFirstChildOfClass("Humanoid") then return true end
if Players:GetPlayerFromCharacter(node) then return true end
end
return false
end
local function getRoot()
local char = LocalPlayer.Character
return char and char:FindFirstChild("HumanoidRootPart")
end
local function nearestPart(origin)
local best, bestDist
for _, inst in ipairs(workspace:GetDescendants()) do
if inst:IsA("BasePart") and not inst.Anchored and not isCharacterPart(inst) then
local d = (inst.Position - origin).Magnitude
if d <= SEARCH_RANGE and (not bestDist or d < bestDist) then
best, bestDist = inst, d
end
end
end
return best, bestDist
end
-- Push for a while, then report how far it actually went and whether the
-- server stopped sending us updates for it (ReceiveAge climbing = ours).
local function pushFor(part, seconds)
local start = part.Position
local maxAge, elapsed = 0, 0
local up = Vector3.new(0, PUSH_SPEED, 0)
while elapsed < seconds and part.Parent do
local dt = RunService.Heartbeat:Wait()
elapsed = elapsed + dt
pcall(function() part.Velocity = up end)
local ok, age = pcall(function() return part.ReceiveAge end)
if ok and type(age) == "number" and age > maxAge then maxAge = age end
end
return (part.Position - start).Magnitude, maxAge, start
end
--=========================================================================
-- run
--=========================================================================
task.spawn(function()
log("== network ownership probe ==")
-- 1. capabilities
local hasHidden = type(sethiddenproperty) == "function"
local hasSimFn = type(setsimulationradius) == "function"
local okAge = pcall(function() return Instance.new("Part").ReceiveAge end)
log(string.format("exec: sethiddenproperty=%s setsimradius=%s ReceiveAge=%s",
tostring(hasHidden), tostring(hasSimFn), tostring(okAge)))
local root = getRoot()
if not root then
log("FAIL: no HumanoidRootPart -- spawn in and re-run")
return
end
-- 2. pick a target
local part, dist = nearestPart(root.Position)
if not part then
log("FAIL: no unanchored non-character part within " .. SEARCH_RANGE .. " studs.")
log("This game has nothing a client could ever move.")
return
end
log(string.format("target: %s (%.0f studs away, size %s)",
part:GetFullName(), dist, tostring(part.Size)))
local box = Instance.new("SelectionBox")
box.Adornee = part
box.Color3 = Color3.fromRGB(90, 200, 255)
box.LineThickness = 0.05
box.Parent = gui
-- 3. raw push, no tricks
log("phase 1: pushing UP for " .. PUSH_SECONDS .. "s, no ownership tricks...")
local moved1, age1, origin = pushFor(part, PUSH_SECONDS)
log(string.format(" moved %.1f studs, peak ReceiveAge %.2f", moved1, age1))
-- 4. push again with the ownership calls applied
log("phase 2: same push, with ReplicationFocus + SimulationRadius...")
pcall(function() LocalPlayer.ReplicationFocus = workspace end)
local boost = RunService.Heartbeat:Connect(function()
if hasHidden then
pcall(function()
sethiddenproperty(LocalPlayer, "SimulationRadius", math.huge)
end)
elseif hasSimFn then
pcall(function() setsimulationradius(math.huge) end)
end
end)
local moved2, age2 = pushFor(part, PUSH_SECONDS)
log(string.format(" moved %.1f studs, peak ReceiveAge %.2f", moved2, age2))
-- 5. stop pushing: does it stay, or snap back?
log("phase 3: released -- watching for snap-back...")
local held = part.Position
local settleStart = tick()
while tick() - settleStart < 2 and part.Parent do
RunService.Heartbeat:Wait()
end
boost:Disconnect()
local drift = (part.Position - held).Magnitude
local backToStart = (part.Position - origin).Magnitude
log(string.format(" drifted %.1f studs after release, now %.1f from where it began",
drift, backToStart))
-- 6. verdict
log("")
local best = math.max(moved1, moved2)
if best < 2 then
log("VERDICT: NO OWNERSHIP. The part never moved.")
log("The server is simulating it and ignoring this client entirely.")
elseif backToStart < 3 and best > 5 then
log("VERDICT: PREDICTION ONLY -- this is the bug you were seeing.")
log("It moved while pushed, then returned to its original spot, so")
log("the movement was local. The server never handed the part over.")
elseif best > 5 then
log("VERDICT: OWNERSHIP WORKS. It moved and stayed moved.")
log("Velocity writes are replicating, so the Optimizer's Network tab")
log("should work in this game -- if it does not, that is a script bug.")
else
log("VERDICT: MARGINAL -- moved " .. string.format("%.1f", best) ..
" studs. Probably fighting the server.")
end
if moved2 > moved1 + 3 then
log("(the ownership calls measurably helped)")
elseif hasHidden then
log("(the ownership calls changed nothing -- SimulationRadius is")
log(" removed on modern Roblox, so this is expected)")
end
task.delay(45, function()
box:Destroy()
gui:Destroy()
end)
end)