local Players = game:GetService("Players") local TextChatService = game:GetService("TextChatService") local Workspace = game:GetService("Workspace") local SCAN_INTERVAL = 0.05 local REQUIRED_GRIEF_TIME = 0.85 local MAX_GAP = 0.30 local DETECTION_DISTANCE = 50 local MIN_DELETIONS = 2 local WARNING_COOLDOWN = 30 local trackedParts = {} local playerData = {} local lastWarnings = {} local function sendChatMessage(message) pcall(function() local channels = TextChatService:WaitForChild("TextChannels") local general = channels:WaitForChild("RBXGeneral") general:SendAsync(message) end) end sendChatMessage("Anti grief script loaded credits to me_meymeyme") local function isDeleteTool(tool) if not tool:IsA("Tool") then return false end local name = tool.Name:lower():gsub("%s+", "") return name == "dtool" or name == "deletetool" or name:find("delete", 1, true) ~= nil or name:find("dtool", 1, true) ~= nil end local function isHoldingDeleteTool(player) local character = player.Character if not character then return false end for _, object in ipairs(character:GetChildren()) do if isDeleteTool(object) then return true end end return false end local function getRoot(player) local character = player.Character if not character then return nil end return character:FindFirstChild("HumanoidRootPart") end local function getClosestDToolPlayer(position) local closestPlayer = nil local closestDistance = DETECTION_DISTANCE for _, player in ipairs(Players:GetPlayers()) do if isHoldingDeleteTool(player) then local root = getRoot(player) if root then local distance = (root.Position - position).Magnitude if distance <= closestDistance then closestDistance = distance closestPlayer = player end end end end return closestPlayer end local function recordDeletion(player) if not playerData[player] then playerData[player] = { deletions = {}, streakStart = nil, lastDeletion = nil } end local data = playerData[player] local now = os.clock() table.insert(data.deletions, now) if data.lastDeletion then local gap = now - data.lastDeletion if gap > MAX_GAP then data.deletions = {now} data.streakStart = nil end end data.lastDeletion = now if not data.streakStart and #data.deletions >= MIN_DELETIONS then data.streakStart = now end end local function scanWorkspace() local currentParts = {} for _, object in ipairs(Workspace:GetDescendants()) do if object:IsA("BasePart") then currentParts[object] = object.Position end end for oldPart, oldPosition in pairs(trackedParts) do if not currentParts[oldPart] then local player = getClosestDToolPlayer(oldPosition) if player then recordDeletion(player) end end end trackedParts = currentParts end local function checkPlayers() local now = os.clock() for player, data in pairs(playerData) do if not player.Parent then playerData[player] = nil lastWarnings[player] = nil continue end if not data.lastDeletion then continue end local gap = now - data.lastDeletion if gap > MAX_GAP then data.streakStart = nil data.deletions = {} data.lastDeletion = nil continue end if data.streakStart then local duration = now - data.streakStart if duration >= REQUIRED_GRIEF_TIME then local lastWarning = lastWarnings[player] or 0 if now - lastWarning >= WARNING_COOLDOWN then lastWarnings[player] = now sendChatMessage( player.Name .. " HAS BEEN DETECTED GRIEFING🚨" ) end data.streakStart = nil data.deletions = {} end end end end for _, object in ipairs(Workspace:GetDescendants()) do if object:IsA("BasePart") then trackedParts[object] = object.Position end end task.spawn(function() while true do scanWorkspace() checkPlayers() task.wait(SCAN_INTERVAL) end end) Players.PlayerRemoving:Connect(function(player) playerData[player] = nil lastWarnings[player] = nil end)