Recording hackers (with scripts)

I had this idea for a moderation system that when my anti-cheat detects a player hacking it will “record a video” of when it detected it. It would work by copying everything in an certain area and then record all the data for a certain time. Then when you want to view the hacker (maybe not a hacker) you could just go to the game, do some command, let it load, and then you could watch what happened (so it replays in the game). This would provide sufficient evidence and would help get rid of all real hackers. Also it might record chat or keypresses. Please input your idea, thanks!

11 Likes

honestly seems good, the only issue would be recording all the data in a certain area, hackers could take advantage of that and overload the data by causing parts to spawn or som idk, since im guessing everything will be recorded on the client and then sent to the server to save to a datastore for viewing

well maybe you could just record all the stuff that the player interacts with so if they spawn stuff it cant be seen on the “recordings”

1 Like

honestly seems pretty good tho, im guessing the best way to do this is:

obviously detect them and then start the recording proccess, to record; the game can save the players info, like coins, level, position, lookvector, and only neccessary info, every, lets say 0.5 to 0.25 seconds depending if the amount of info cant load fast enough, and then it will be transfered to the server via remote event, which immedietly sends the info to a datastore, which would be the players, and could maybe be called “DetectionInfo” which would hold each value in a table, then obviously if you want to load it, first you create a rig, and then make a script get each position value, and either tween to it every 0.25 to 0.5 seconds depending how long it was before, or to just teleport to it, and then it will print out each value if its changed

2 Likes

thanks, I was thinking about doing it on server, because the client could change all of the data to nothing

yeah its better to detect everything on the client first;

since hackers can only run things on the client anyway, and maybe even add a feature where it also records on the server to match things up to see if lets say hes supposed to have a certain amount of something, and then send it to the server

thanks for that great idea! I’ll make a client part which sends lots of detailed data (not too much) and a server part which saves low data (just enough to tell whats happening). Also why wouldn’t I want to save the data on the server, because I’m not doing any calculations, just saving cframes (and other stuff)

the only question is how would you store the recorded data. Obviously storing the “Video” using datastoreservice is impossible. encoding and decoding the data would still take up resources, but it shouldn’t matter if it’s used just to watch a playback.

the exploiter can just make it so it doesn’t send data from the client.

Make a server check, if client stopped sending important data, kick the player?

1 Like

this is technically impossible without exploiting, unless the client lags so much it didn’t send the data.

1 Like

OR the exploiter can send data that isn’t suspicious while still exploiting. also it’s hard to detect exploiting (except for common ones like flying or teleporting)

1 Like

fair enough…

if you really need to defend the game you can prevent some injections by garbage collection (aka Citadel AC)

uhhh? cheaters usually aren’t problem if you can write safe code, make your game safe instead, learn about how remotes work and how to prevent basic exploits, creating comparison debounce or checking if player have item before saving it can be 100x better than recording video and wasting performance for this

2 Likes

Everyone I got it to work!!! @0786ideal @Fan_Len4ika1Tvink123 @MikeartsRBLX @p49p0 @makeitxx

2 Likes

The time and frames are changeable

Server script:

local DataStoreService = game:GetService("DataStoreService")
local recordStore = DataStoreService:GetDataStore("RecordStore")
local Players = game:GetService("Players")
local recordingDuration = 10  -- Record for 10 seconds
local recordingInterval = .1   -- Save data every second
local regionSize = Vector3.new(50, 50, 50)  -- Define region size around cheater

-- Anti-Cheat system
local maxSpeed = 20  -- Example speed limit


local function generateUniqueId()
	local random = Random.new()
	return tostring(random:NextInteger(1, 100000000)) .. tostring(tick())
end



-- Function to save block and player data around a cheater
local function saveBlocksAndPlayersAroundCheater(player)
	local position = player.Character.HumanoidRootPart.Position
	local region = Region3.new(position - (regionSize / 2), position + (regionSize / 2))
	local parts = workspace:FindPartsInRegion3(region, nil, math.huge)
	local frameData = { blocks = {}, players = {} }

	for _, part in ipairs(parts) do
		if part:IsA("BasePart") then
			-- Generate a unique ID for each part if it doesn't have one already
			part:SetAttribute("UniqueId", part:GetAttribute("UniqueId") or generateUniqueId())

			-- Get the mesh if the part has one (MeshPart or SpecialMesh)
			local meshId, meshType, textureId
			if part:IsA("MeshPart") then
				meshId = part.MeshId
				textureId = part.TextureID
			elseif part:FindFirstChild("SpecialMesh") then
				local mesh = part:FindFirstChild("SpecialMesh")
				meshId = mesh.MeshId
				meshType = mesh.MeshType.Name
				textureId = mesh.TextureId
			end

			-- Save part properties, including the unique ID
			table.insert(frameData.blocks, {
				uniqueId = part:GetAttribute("UniqueId"),  -- Save the unique ID
				name = part.Name,
				size = {part.Size.X, part.Size.Y, part.Size.Z},
				position = {part.Position.X, part.Position.Y, part.Position.Z},
				color = part.BrickColor.Name,
				material = part.Material.Name,
				transparency = part.Transparency,
				reflectance = part.Reflectance,
				meshId = meshId,
				textureId = textureId,
				meshType = meshType
			})
		end

		-- Save player information
		if part.Parent:FindFirstChild("Humanoid") then
			local playerName = part.Parent.Name
			if not table.find(frameData.players, playerName) then
				table.insert(frameData.players, playerName)
			end
		end
	end

	return frameData
end



-- Function to start recording after detecting a cheater
local function startRecording(player)
	local data = {}
	local startTime = tick()

	while tick() - startTime < recordingDuration do
		-- Save block and player data
		local frameData = saveBlocksAndPlayersAroundCheater(player)
		table.insert(data, frameData)
		wait(recordingInterval)
	end

	-- Save data to DataStore after recording
	local success, err = pcall(function()
		recordStore:SetAsync(player.Name, data)
	end)

	if success then
		print("Recording saved successfully!")
	else
		warn("Error saving data: " .. err)
	end
end

-- Anti-Cheat: Detect if player is cheating by exceeding speed limit
Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		local humanoid = character:WaitForChild("Humanoid")

		while humanoid do
			if humanoid.WalkSpeed > maxSpeed then
				print(player.Name .. " is possibly hacking!")
				startRecording(player)
			end
			wait(1)
		end
	end)
end)

-- Function to load recording data from DataStore
local function loadRecording(player)
	local success, data = pcall(function()
		return recordStore:GetAsync(player.Name)
	end)

	if success and data then
		print("Recording loaded successfully!")
		return data
	else
		warn("Error loading data or no recording found.")
		return nil
	end
end

-- Chat command to load recording
Players.PlayerAdded:Connect(function(player)
	player.Chatted:Connect(function(message)
		if message == "!load" then
			local data = loadRecording(player)
			if data then
				-- Send data to the player's client to start playback
				game.ReplicatedStorage:WaitForChild("PlaybackControl"):FireClient(player, data)
			end
		end
	end)
end)

Local script (to render it):

local currentFrame = 1
local isPaused = false
local data = {}  -- Will be populated with the loaded recording data

-- Clear all parts
function clearParts(loadedParts)  -- Changed from local to global
	-- Ensure loadedParts is a table
	if loadedParts == nil then
		warn("loadedParts is nil, expected a table.")
		return  -- Exit the function if loadedParts is nil
	end

	for _, part in pairs(workspace:GetChildren()) do
		-- Check if the part's UniqueId matches any in the loadedParts
		if part:IsA("BasePart") and table.find(loadedParts, part:GetAttribute("UniqueId")) then
			part:Destroy()
		end
	end
end

-- Pause playback
function pausePlayback()
	isPaused = true
end

-- Play playback
function playPlayback()
	isPaused = false
	startPlayback(data)  -- Resume where it left off
end

-- Create parts from frame data
function createParts(frameData)
	for _, partInfo in pairs(frameData.blocks) do
		-- Check if a part with the same UniqueId exists
		local partExists = false
		for _, existingPart in pairs(workspace:GetChildren()) do
			if existingPart:IsA("BasePart") and existingPart:GetAttribute("UniqueId") == partInfo.uniqueId then
				partExists = true
				break
			end
		end

		-- Only create the part if it doesn't already exist
		if not partExists then
			local part = Instance.new("Part")

			-- Apply saved properties
			part.Size = Vector3.new(partInfo.size[1], partInfo.size[2], partInfo.size[3])
			part.Position = Vector3.new(partInfo.position[1], partInfo.position[2], partInfo.position[3])
			part.BrickColor = BrickColor.new(partInfo.color)
			part.Material = Enum.Material[partInfo.material]
			part.Transparency = partInfo.transparency
			part.Reflectance = partInfo.reflectance
			part.Anchored = true  -- Ensure the part is anchored

			-- Handle mesh properties
			if partInfo.meshId then
				if part:IsA("MeshPart") then
					part.MeshId = partInfo.meshId
					part.TextureID = partInfo.textureId
				else
					local mesh = Instance.new("SpecialMesh", part)
					mesh.MeshId = partInfo.meshId
					if partInfo.meshType then
						mesh.MeshType = Enum.MeshType[partInfo.meshType]
					end
					mesh.TextureId = partInfo.textureId
				end
			end

			-- Assign the unique ID to the new part
			part:SetAttribute("UniqueId", partInfo.uniqueId)
			part.Parent = workspace
		end
	end
end

-- Start playback
function startPlayback(data)
	currentFrame = 1

	while currentFrame <= #data and not isPaused do
		-- Collect the unique IDs from the current frame's data
		local loadedParts = {}
		for _, partInfo in ipairs(data[currentFrame].blocks) do
			table.insert(loadedParts, partInfo.uniqueId)
		end

		clearParts(loadedParts)  -- Pass the loadedParts to clearParts
		createParts(data[currentFrame])  -- Create parts for the current frame
		wait(0.1)  -- Frame delay, adjust as needed
		currentFrame = currentFrame + 1
	end
end

-- Next frame
function nextFrame()
	if currentFrame < #data then
		-- Collect unique IDs for the current frame
		local loadedParts = {}
		for _, partInfo in ipairs(data[currentFrame].blocks) do
			table.insert(loadedParts, partInfo.uniqueId)
		end

		clearParts(loadedParts)  -- Pass the loadedParts to clearParts

		currentFrame = currentFrame + 1  -- Move to the next frame

		clearParts(loadedParts)  -- Pass the loadedParts to clearParts
		createParts(data[currentFrame])  -- Create parts for the new current frame
	end
end

-- Previous frame
function backFrame()
	if currentFrame > 1 then
		-- Collect unique IDs for the current frame
		local loadedParts = {}
		for _, partInfo in ipairs(data[currentFrame].blocks) do
			table.insert(loadedParts, partInfo.uniqueId)
		end

		clearParts(loadedParts)  -- Pass the loadedParts to clearParts

		currentFrame = currentFrame - 1  -- Move to the previous frame

		clearParts(loadedParts)  -- Pass the loadedParts to clearParts
		createParts(data[currentFrame])  -- Create parts for the new current frame
	end
end

-- Setup button events (GUI button events)
local playButton = script.Parent:WaitForChild("Play")
local pauseButton = script.Parent:WaitForChild("Pause")
local nextButton = script.Parent:WaitForChild("Forward")
local backButton = script.Parent:WaitForChild("Backward")

playButton.MouseButton1Click:Connect(playPlayback)
pauseButton.MouseButton1Click:Connect(pausePlayback)
nextButton.MouseButton1Click:Connect(nextFrame)
backButton.MouseButton1Click:Connect(backFrame)

-- Handle playback control data from server
game.ReplicatedStorage:WaitForChild("PlaybackControl").OnClientEvent:Connect(function(recordingData)
	data = recordingData
	startPlayback(data)
end)
1 Like

I’ll be adding more, mostly fixing somethings

1 Like

also I know the anti cheat doesn’t work (it checks for server value). I’m just using it to test

i think that’s a great idea mate.
i feel like if you were doing it video related it would impact performance of the game a lot more but i could be wrong. my idea is that you could clone the character model of every player in the game; only viewable for you as the owner and focus the camera mode to that hacker’s character model upon command request. (:view player_charactermodel)

i suppose then you could store the CFrame with the time using math.osclock or something. the CFrame will store the players rotation and position so that you can see if they’re flying, etc.

that’s just my idea and how i would go about it. hopefully it helps!

there’s a tutorial on how you would do something like this:

wow, thanks a lot! I think about this

1 Like