Загрузка данных
--[[
PLAYER STATE WATCHER v1
Delta / Roblox client lightweight build.
Purpose:
- Does NOT dump current values.
- Watches player-related replicated state.
- Prints ONLY changes in real time.
- Tracks ValueBase objects and Attributes.
- Dynamically attaches to newly created objects.
]]
print("[STATE WATCHER] 01 script entered")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
if not player then
warn("[STATE WATCHER] LocalPlayer unavailable")
return
end
print("[STATE WATCHER] 02 LocalPlayer=" .. player.Name)
local watchedInstances = setmetatable({}, {__mode = "k"})
local watchedAttributes = setmetatable({}, {__mode = "k"})
local connections = {}
local function safePath(obj)
local ok, result = pcall(function()
return obj:GetFullName()
end)
return ok and result or tostring(obj)
end
local function formatValue(v)
local t = typeof(v)
if t == "string" then
if #v > 120 then
return string.format("%q...", v:sub(1, 117))
end
return string.format("%q", v)
elseif t == "number" then
return tostring(v)
elseif t == "boolean" then
return tostring(v)
elseif t == "Instance" then
return safePath(v)
end
return tostring(v)
end
local function numericDelta(oldValue, newValue)
if typeof(oldValue) == "number" and typeof(newValue) == "number" then
local d = newValue - oldValue
if d > 0 then
return " (+" .. tostring(d) .. ")"
elseif d < 0 then
return " (" .. tostring(d) .. ")"
end
end
return ""
end
local function emit(kind, obj, name, oldValue, newValue)
local line = string.format(
"[STATE CHANGE] %s | %s | %s: %s -> %s%s",
kind,
safePath(obj),
tostring(name),
formatValue(oldValue),
formatValue(newValue),
numericDelta(oldValue, newValue)
)
print(line)
if typeof(oldValue) == "number" and typeof(newValue) == "number" then
local d = newValue - oldValue
if math.abs(d) > 0 then
print(string.format(
"[STATE DELTA] %s | %s | delta=%s",
tostring(name),
safePath(obj),
tostring(d)
))
end
end
end
local function watchValueBase(obj)
if watchedInstances[obj] then
return
end
if not obj:IsA("ValueBase") then
return
end
watchedInstances[obj] = true
local ok, lastValue = pcall(function()
return obj.Value
end)
if not ok then
return
end
table.insert(connections, obj:GetPropertyChangedSignal("Value"):Connect(function()
local readOk, newValue = pcall(function()
return obj.Value
end)
if not readOk then
return
end
if newValue ~= lastValue then
local oldValue = lastValue
lastValue = newValue
emit("VALUE", obj, obj.Name, oldValue, newValue)
end
end))
end
local function watchAttributes(obj)
if watchedAttributes[obj] then
return
end
watchedAttributes[obj] = true
local previous = {}
local ok, attrs = pcall(function()
return obj:GetAttributes()
end)
if ok then
for name, value in pairs(attrs) do
previous[name] = value
end
end
table.insert(connections, obj.AttributeChanged:Connect(function(name)
local readOk, newValue = pcall(function()
return obj:GetAttribute(name)
end)
if not readOk then
return
end
local oldValue = previous[name]
if oldValue ~= newValue then
previous[name] = newValue
emit("ATTRIBUTE", obj, name, oldValue, newValue)
end
end))
end
local function attachObject(obj)
pcall(watchValueBase, obj)
pcall(watchAttributes, obj)
end
local function attachTree(root)
attachObject(root)
local ok, descendants = pcall(function()
return root:GetDescendants()
end)
if not ok then
return
end
for _, obj in ipairs(descendants) do
attachObject(obj)
end
end
local roots = {
player
}
local character = player.Character
if character then
table.insert(roots, character)
end
local playerGui = player:FindFirstChildOfClass("PlayerGui")
if playerGui then
table.insert(roots, playerGui)
end
local backpack = player:FindFirstChildOfClass("Backpack")
if backpack then
table.insert(roots, backpack)
end
for _, root in ipairs(roots) do
attachTree(root)
table.insert(connections, root.DescendantAdded:Connect(function(obj)
attachObject(obj)
end))
end
table.insert(connections, player.CharacterAdded:Connect(function(char)
print("[STATE WATCHER] Character changed -> " .. safePath(char))
attachTree(char)
table.insert(connections, char.DescendantAdded:Connect(function(obj)
attachObject(obj)
end))
end))
-- Catch leaderstats/backpack/gui appearing after watcher startup.
table.insert(connections, player.ChildAdded:Connect(function(child)
if child.Name == "leaderstats"
or child:IsA("Backpack")
or child:IsA("PlayerGui")
then
attachTree(child)
table.insert(connections, child.DescendantAdded:Connect(function(obj)
attachObject(obj)
end))
end
end))
print("[STATE WATCHER] READY")
print("[STATE WATCHER] Watching player ValueBase + Attributes. Console will stay quiet until something changes.")