[FREE] Anti-Trolling Anti-Assault System

Get on Creator Store

I’ve noticed that Roblox has become rampant with trolls doing inappropriate behaviors on a platform composed mostly of a younger audience. I wanted to tackle this problem and created an automatic troll detection and prevention system for Roblox games. This script detects players spamming forward and backward (W/S key spamming) near another player and automatically kicks them after a sustained period. This helps prevent harassment, trolling, and inappropriate behavior in your game.


How the System Works

Detection Mechanism

  1. Monitors Movement Direction

    • Uses CFrame LookVector to determine if a player is moving forward or backward relative to their current facing direction.
    • Uses dot product calculations to assign movement a value:
      • +1 = Forward
      • -1 = Backward
      • 0 = Neither (standing still or moving sideways)
  2. Detects Rapid Toggling Between Forward and Backward

    • If a player rapidly switches between forward and backward movement, the system recognizes this as toggling.
    • If toggling continues for longer than REQUIRED_TOGGLE_TIME, the system registers a detection.
  3. Checks if Another Player is in Front

    • Uses Raycasting (Enum.RaycastFilterType.Exclude) to determine if another player’s character is within CHECK_DISTANCE studs ahead.
    • If no player is detected in front, the toggling does not count and resets.
  4. Tracks Detections and Issues Warnings

    • Every time a valid detection occurs, the player’s detection count increases.
    • If the detection count is below DETECTIONS_REQUIRED, a warning is logged, and tracking resets for further offenses.
  5. Punishes Offenders

    • If the player reaches DETECTIONS_REQUIRED, they are kicked from the server (default behavior).
    • The punishment logic is handled by the PunishPlayer function, which can be modified for alternative actions (e.g., bans).

Demonstration


Customization Options

You can tweak the following values in the script to adjust sensitivity:

Setting Default Description
REQUIRED_TOGGLE_TIME 1 Time (in seconds) a player must continuously toggle forward/backward near another player before being detected.
CHECK_DISTANCE 3 Distance (in studs) to check for another player in front of the suspected troll.
TOGGLE_COOLDOWN 0.1 Minimum time (in seconds) between direction flips to prevent false detections from minor movements.
DOT_THRESHOLD 0.4 Sensitivity of forward/backward detection. Lower values mean stricter detection of movement changes.
DETECTIONS_REQUIRED 1 Number of times a player must be detected before they are punished. Allows warnings before action is taken.

Installation

To integrate this system into your game:

  1. Open Roblox Studio and navigate to Explorer → ServerScriptService.
  2. Create a new Script inside ServerScriptService.
  3. Paste the provided script (see below).
  4. Modify detection settings (e.g., CHECK_DISTANCE, TOGGLE_COOLDOWN) if needed.

Full Script

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

-- Configuration values
local REQUIRED_TOGGLE_TIME = 1      -- seconds of continuous toggling while someone is in front
local CHECK_DISTANCE = 3            -- studs in front of character to raycast
local TOGGLE_COOLDOWN = 0.1         -- minimum gap (seconds) between flips to count as new toggle
local DOT_THRESHOLD = 0.4           -- threshold for dot product (forward/backward detection)
local DETECTIONS_REQUIRED = 1		-- number of detections required to punish the player

--------------------------------------------------------------------------------
-- A dictionary to store each player's "monitoring" connection.
-- Key = Player object, Value = a single Heartbeat connection.
--------------------------------------------------------------------------------
local playerConnections = {}

--------------------------------------------------------------------------------
-- 1) Punishment function (kicks player by default). 
--    You can modify this to do something else (e.g. ban).
--------------------------------------------------------------------------------
local function PunishPlayer(player: Player)
	player:Kick("Trolling detected: Inappropriate forward-backward spamming.")
	warn(("Player %s has been kicked for spamming forward-backward."):format(
		player.Name
		))
end

--------------------------------------------------------------------------------
-- 2) Called every time the script detects a forward-backward spam event 
--    that meets the REQUIRED_TOGGLE_TIME. We decide whether to punish or warn.
--------------------------------------------------------------------------------
local function OnDetect(player, detectionCount)
	if detectionCount >= DETECTIONS_REQUIRED then
		-- If the detection count reaches or exceeds threshold, punish
		PunishPlayer(player)
		playerConnections[player]:Disconnect()
	else
		-- Otherwise, just warn (print to console or do something else)
		warn(("Player %s has been detected spamming forward-backward (%d/%d)."):format(
			player.Name, detectionCount, DETECTIONS_REQUIRED
			))
	end
end

--------------------------------------------------------------------------------
-- 3) Determines if the character is moving forward, backward, or neither.
--    Returns:
--      +1 -> Forward
--      -1 -> Backward
--       0 -> Neither strongly forward nor backward
--------------------------------------------------------------------------------
local function GetMovementDirectionMode(rootPart, movementVector, dotThreshold)
	local forwardVector = rootPart.CFrame.LookVector
	local dot = forwardVector:Dot(movementVector)

	if dot > dotThreshold then
		return 1   -- forward
	elseif dot < -dotThreshold then
		return -1  -- backward
	else
		return 0   -- neither
	end
end

--------------------------------------------------------------------------------
-- 4) Checks if direction flipped from forward to backward or backward to forward.
--------------------------------------------------------------------------------
local function DidFlipDirections(oldMode, newMode)
	return (oldMode == 1 and newMode == -1) or (oldMode == -1 and newMode == 1)
end

--------------------------------------------------------------------------------
-- 5) Raycast in front of this character to see if another player's character is there.
--    Returns true if a player's Humanoid is detected in front within 'distance' studs.
--------------------------------------------------------------------------------
local function IsPlayerInFront(character, distance)
	local humanoid = character:FindFirstChildWhichIsA("Humanoid")
	if not humanoid or not humanoid.RootPart then return false end

	local origin = humanoid.RootPart.Position
	local forwardVector = humanoid.RootPart.CFrame.LookVector
	local direction = forwardVector * distance

	-- We don't want to hit our own character, so exclude it
	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = {character}
	rayParams.FilterType = Enum.RaycastFilterType.Exclude

	local rayResult = workspace:Raycast(origin, direction, rayParams)
	if rayResult and rayResult.Instance then
		local hitPart = rayResult.Instance
		local hitCharacter = hitPart:FindFirstAncestorWhichIsA("Model")
		if hitCharacter then
			local hitHumanoid = hitCharacter:FindFirstChildWhichIsA("Humanoid")
			if hitHumanoid then
				return true
			end
		end
	end

	return false
end

--------------------------------------------------------------------------------
-- 6) Main function that creates and returns a Heartbeat connection for monitoring.
--    Called when the player's Character is added.
--    We store the 'connection' in 'playerConnections'.
--------------------------------------------------------------------------------
local function StartMonitoringCharacter(player, character)
	if not character then return nil end

	local humanoid = character:FindFirstChildWhichIsA("Humanoid")
	if not humanoid or not humanoid.RootPart then return nil end

	-- Variables to track toggling
	local lastDirectionMode = 0
	local isToggling = false
	local toggleStartTime = 0
	local lastToggleTime = 0

	-- NEW: Track how many times this player has been detected
	local detectionCount = 0

	-- Create a new Heartbeat connection
	local connection
	connection = RunService.Heartbeat:Connect(function()
		-- If the character or humanoid is gone, disconnect
		if not character.Parent or humanoid.Health <= 0 then
			connection:Disconnect()
			return
		end

		local movementVector = humanoid.MoveDirection
		local directionMode = GetMovementDirectionMode(humanoid.RootPart, movementVector, DOT_THRESHOLD)

		local flipped = DidFlipDirections(lastDirectionMode, directionMode)
		local currentTime = os.clock()

		if flipped and (currentTime - lastToggleTime) > TOGGLE_COOLDOWN then
			if not isToggling then
				isToggling = true
				toggleStartTime = currentTime
			end
			lastToggleTime = currentTime
		end

		-- If toggling, check if there's a player in front
		if isToggling then
			local playerInFront = IsPlayerInFront(character, CHECK_DISTANCE)
			if playerInFront then
				-- If the toggling + player-in-front scenario lasts too long, increment detection
				if (currentTime - toggleStartTime) >= REQUIRED_TOGGLE_TIME then
					detectionCount += 1
					OnDetect(player, detectionCount)

					-- If not punished yet, reset toggling as requested
					if detectionCount < DETECTIONS_REQUIRED then
						isToggling = false
					end
				end
			else
				-- No one in front; reset toggling if enough time has passed
				if (currentTime - lastToggleTime) > 1 then
					isToggling = false
				end
			end
		end

		lastDirectionMode = directionMode
	end)

	return connection
end

--------------------------------------------------------------------------------
-- 7) Listen for Players joining. For each character spawn, ensure we handle 
--    the universal dictionary logic: if there's an existing connection, 
--    disconnect it before making a new one.
--------------------------------------------------------------------------------
Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		-- If there's already a stored connection for this player, disconnect it
		if playerConnections[player] then
			playerConnections[player]:Disconnect()
			playerConnections[player] = nil
		end

		-- Create a new monitoring connection for this character
		local newConnection = StartMonitoringCharacter(player, character)
		playerConnections[player] = newConnection
	end)
end)

--------------------------------------------------------------------------------
-- 8) Clean up when a Player leaves. If there's a connection in the dictionary,
--    disconnect it and remove from the dictionary so we don't leak memory.
--------------------------------------------------------------------------------
Players.PlayerRemoving:Connect(function(player)
	if playerConnections[player] then
		playerConnections[player]:Disconnect()
		playerConnections[player] = nil
	end
end)

Changelog

Current Release

How to Test

  1. Run the game in a Local Server in Roblox Studio.
  2. Move forward and backward rapidly (spam W and S).
  3. Position another player/NPC in front of you and repeat the movement.
  4. If spamming continues for more than 1 second, you will be kicked.

Notes & Additional Information

  • This system only activates if another player is directly in front of the offender.
  • The toggle detection ensures minor movements aren’t falsely flagged.
  • The universal dictionary (playerConnections) prevents memory leaks by ensuring only one active connection per player.

Feedback & Support

Have suggestions, improvements, or found a bug? Feel free to share your thoughts in the comments.

22 Likes

Oh, this seems very useful, I’ll definitely look into this system!

3 Likes

Really nice work with this, great tool to handle trolls!

2 Likes

What? What difference does that make between nested functions all under CharacterAdded than utilizing tables/dictionaries? Because since late-2024, Roblox automatically destroys and cleans any character objects/connections when the player leaves/respawns

3 Likes

This is the first time I’ve seen something like this here, nice.

1 Like

This seems exaggerated honestly. only 1 second, first of all (not an issue since you can change that) but it doesnt check if the raycast is in the backside of the character

If im doing it to the front of the player am i still kicked?

Nice system! There’s stuttering on the other side a bit don’t know what it is, but this can be funny to trolls as they get trolled.

1 Like

It’s a Heartbeat connection, are you sure that would be reset on the players leaving or respawning?

If yes, then I’ll update the script accordingly

It includes the front of players as well because trolls will do these things to any side of the player. I’ve seen it happen plenty of times.

If you would prefer to make changes to how the script works, you’re free to do that

Version 1.1

  • Added DETECTIONS_REQUIRED Setting – Allows multiple detections before punishing a player (default: 1).
  • New PunishPlayer(player) Function – Handles punishment logic (default: kick, but can be customized).
  • New OnDetect(player, detectionCount) Function – Tracks detections and logs warnings before punishing.
  • Toggling Resets After Warnings – If a player hasn’t reached the DETECTIONS_REQUIRED threshold yet, their toggling variable resets.

This update improves customization and flexibility, letting you control how and when players get punished for repeated trolling behavior.

Note: DETECTIONS_REQUIRED was set to 3 for this demonstration.

1 Like