Anti-j#rk | Quickly stop exploiters from this popular cheat

To simply explain this is an inappropriate cheat which shows players characters playing an inappropriate animation (taking their hand and you know what).

This is the situation; one of our moderators recently got banned from our experience after running this script with their exploit software, it’s an understatement to say that we’re heartbroken and furious about this.

Not to forget mentioning that they’ve recorded themselves while doing this with a “friend”.
Actions have consequences, and we demoted this individual as soon as we could.

Then we came to the conclusion that blocking this is necessary, a bunch of minors play our experience every day and we do not tolerate this being shown to them.

How does it work?

It simply works by checking if the animation the player is playing matches animations used in this cheat. Since their “cheat” works by playing a Roblox animation then looping it at a time range where the hand is close to the legs.

This entire resource is basically a “signature” based approach, the code makes sure the animation playing actually sort of matches the blacklisted parameters (e.g. speed, start time).

Then there is the whole "confidence" concept, which makes sure the animation is actually being looped in that bad time-position, this significantly decreases false-positives to the point where it’s really rare to get one.

You can implement your punishment logic when there is a detection within the onDetection() function, the default behavior is kicking the player.

Usage

It’s pretty basic, and fully serversided.
Since animations played on the client replicate to the server.

  • Make a script in “ServerScriptService” and name it whatever you want.

  • Then paste the script inside there and test for false-positives, increment CONFIDENCE_LIMITER at line 31 if you are experiencing false-positives.

    • big values (eg. 300) causes it too take too long to detect.
    • low values (eg. 50) may cause false-positives.

                 RobloxStudioBeta_FFlwPuDRwk

  • Do not edit the R6_ANIM*_BLACKLISTED_.. and R15_BLACKLISTED_.. constants. These are fine-tuned to the cheat’s parameters.

This script both detects the R15 and the R6 version of the cheat, though the R6 detection is a bit finicky as the R6 version of the cheat just spams two animations at rapid succession.

--# Author: SyntaxMenace #--

--[[
	This script attempts to stop an animation exploit which is labeled "j**king"
	
	It has gone through testing but please test this in your own place yourself
	if your game has any of the following animations that are played on characters:
	
		rbxassetid://698251653 => Bug Net - R15 Swing
		rbxassetid://72042024 => R6, RapidSlash
		rbxassetid://168268306 => R6, Space_Sword_Swing_1
	
	Lag or unpredictable environment factors might cause minor differences in timing and risk false positives.
	Though this is shouldn't occur as long as "CONFIDENCE_LIMITER" constant is above 100.
	
	"I applaud the simplicity of this approach because It does exactly
	what it’s designed to do for a known exploit: it zeroes in on specific
	suspicious animations that no normal player should be running." - ChatGPT o1
]]

-- Update 1/20/2024: Fixed a memory leak.

local Players = game:GetService("Players")

local R15_BLACKLISTED_START_TIME = 0.579	--> Don't change this.
local R15_BLACKLISTED_END_TIME = 0.72		--> Don't change this.

local R6_ANIM1_BLACKLISTED_START_TIME = 0.5	--> Don't change this.
local R6_ANIM2_BLACKLISTED_START_TIME = 1.0 --> Don't change this.

local CONFIDENCE_LIMITER = 150 --> This value should NEVER go below 100
-- limits how many iterations it will take to make sure this animation is being played.

CONFIDENCE_LIMITER = math.max(CONFIDENCE_LIMITER, 100)
CONFIDENCE_LIMITER = math.min(CONFIDENCE_LIMITER, 300)

local function onDetected(Player: Player)
	--// Basically your punishment logic here
	--// I highly recommend a BanWave logic, but quickly banning or kicking also works.

	Player:Kick("You've been kicked for doing the no-no!") -- "You've been kicked for jorking!"
end


local function addToDetectionLoop_R15(player: Player, animationTrack: AnimationTrack)
	local timePositions = {}

	local confidence = 0
	while animationTrack.IsPlaying and confidence <= CONFIDENCE_LIMITER do
		task.wait(1 / 24)

		local currentTimePos = animationTrack.TimePosition

		if not table.find(timePositions, currentTimePos) then
			table.insert(timePositions, currentTimePos)
		end

		confidence += 1
	end

	--// Handle the case where animation ended before we are confident enough to give a detection.
	if confidence < CONFIDENCE_LIMITER then
		return "Animation ended too soon."
	end

	table.sort(timePositions)
	
	if not timePositions[1] or not timePositions[#timePositions] then
		return "Time positions are corrupt!" --// might be an edge case, not taking the risk of errors.
	end

	local suspiciousStartTime = math.abs(timePositions[1] - R15_BLACKLISTED_START_TIME) < 0.05
	local suspiciousEndTime = math.abs(timePositions[#timePositions] - R15_BLACKLISTED_END_TIME) < 0.1

	-- print( math.abs(timePositions[1] - R15_BLACKLISTED_START_TIME) )
	-- print( math.abs(timePositions[#timePositions] - R15_BLACKLISTED_END_TIME), timePositions[#timePositions] )

	if suspiciousStartTime and suspiciousEndTime then
		onDetected(player)
	end
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		local Humanoid = character:WaitForChild("Humanoid", 15) :: Humanoid

		if not Humanoid then return end

		local r6_animations = {
			countAnimation1 = 0,
			countAnimation2 = 0
		}

		local animationConnection

		animationConnection = Humanoid.AnimationPlayed:Connect(function(animationTrack)
			local animationId = animationTrack.Animation.AnimationId	
			local startTime = animationTrack.TimePosition

			if animationId == "rbxassetid://698251653" then
				--// The r15 version of this cheat works by constantly playing the animation in a bad time-frame.
				-- 0.5799 .. 0.6800

				-- warn("Playing the suspicious animation")

				if math.abs(startTime - R15_BLACKLISTED_START_TIME) > 0.05 then
					return "Did not start at blacklisted time-frame."
				elseif math.abs(animationTrack.Speed - 0.4) > 0.001 then
					return "Either this animation is not the one we're looking for, or the cheat was updated."
				end

				addToDetectionLoop_R15(player, animationTrack)
			elseif animationId == "rbxassetid://72042024" or animationId == "rbxassetid://168268306" then
				--// The r6 version of this cheat works by constantly playing two animations in rapid succession.

				-- Guard clauses for animations that don't have the blacklisted start times.
				if animationId == "rbxassetid://72042024" and math.abs(startTime - R6_ANIM1_BLACKLISTED_START_TIME) > 0.1 then
					return "Animation1 did not start at blacklisted frame-time.", startTime
				elseif animationId == "rbxassetid://168268306" and math.abs(startTime - R6_ANIM2_BLACKLISTED_START_TIME) > 0.1 then
					return "Animation2 did not start at blacklisted frame-time.", startTime
				elseif animationTrack.Speed ~= 1 then
					return "Animation is either not the animation we're looking for, or the cheat was updated."
				end

				if animationId == "rbxassetid://72042024" then
					r6_animations.countAnimation1 += 1
				elseif animationId == "rbxassetid://168268306" then
					r6_animations.countAnimation2 += 1
				end

				local confident_Animation1IsPlaying = r6_animations.countAnimation1 >= CONFIDENCE_LIMITER / 5
				local confident_Animation2IsPlaying = r6_animations.countAnimation2 >= CONFIDENCE_LIMITER / 3

				if confident_Animation1IsPlaying and confident_Animation2IsPlaying then
					animationConnection:Disconnect()

					onDetected(player)
				else
					--// Debugging, uncomment if you're expanding this script and want to test.
					-- warn("Animation1 Count", r6_animations.countAnimation1, "confident animation1 is playing rapidly:", confident_Animation1IsPlaying)
					-- warn("Animation2 Count", r6_animations.countAnimation2, "confident animation2 is playing rapidly:", confident_Animation2IsPlaying)
				end
			end
		end)


		-- This is probably not the best way but eh, I got lazy
		-- wont cause performance issues anyway soo
		while task.wait(60 + CONFIDENCE_LIMITER / 10) and character:IsDescendantOf(game) do
			r6_animations.countAnimation1 = 0
			r6_animations.countAnimation2 = 0
		end
	end)
end)

If you wanted to do this in the client you could just check if player owns a tool with no children, then checking if the tool’s name is whatever this tool is named.
But devforum members will instantly flock to your post saying that “client detections can be bypassed in 0.000000000000000001 nanoseconds” like mosquitoes being attracted by the brightest power of light in the universe. Without realizing one simply does not just have one layer of anti-cheat.
:derp:

36 Likes

This isn’t really an anti-cheat but more of an “average pdf file behavior” remover.

Also:

CONFIDENCE_LIMITER = math.max(CONFIDENCE_LIMITER, 100)
CONFIDENCE_LIMITER = math.min(CONFIDENCE_LIMITER, 300)

:skull:
Peak programming.
Because of those two lines I find this entire resource low quality just because of those two lines.

7 Likes

if it works, then it works. lol

6 Likes

No! That’s not how it works! This is like such a DUMB idea to use TWO lines just to clamp a number! Why can’t OP just do:

CONFIDENCE_LIMITER = math.clamp(CONFIDENCE_LIMITER,100,300)

!!!

5 Likes

ok but my statement still stands

3 Likes

seriously tho, 2 lines that may not look the fanciest (but works) isn’t gonna make the entire code ‘low quality’, speaking from perspective

4 Likes

Used to be just math.max(CONFIDENCE_LIMITER, 100) then I added the min 300 after noticing someone might just try to “make it have no false-positives”.

It makes no sense to hate on some code because I’ve done another way of basically doing the same logic, not reducing readability nor performance in those two lines.
Minimalism isn’t just always the pinnacle of programming. :+1:

And the thing is just making sure the constants are staying in range.
:sick:

3 Likes

Wow SyntaxMenace I love you & you are awesome!! Thank you for providing us this awesome script that is totally awesomely written and awesome did I say awesome??

I actually recently had an encounter with someone like that.
Thank you for providing a scirpt that prevents these children from doing this.

4 Likes

I may be stupid but, can’t you just check the animation that is played on the server using the animationplayed event? Then check the animation Id. And if the ID doesn’t match any of your game’s animation IDs then just ban the user?

You can do this in a workspace descendantadded event right? Just check for either a humanoid or animator or animationcontroller that has been added.

workspace.DescentAdded:Connect(function(d)
if d:IsA("Animator") then
d.AnimationPlayed:Connect(function(track)
if track.AnimationId ~= "MyIDs" then
ban them
end
end)
3 Likes

Sadly, hackers can hack.

You can also simply adjust the position of the hand/arm without animation.

3 Likes

But they’d still have to play the animation on the animator right?? Or if they wanna be smart they might make a new one but that can be detected on the event.

Oh wait I get it, the server won’t know the client made the animator. My apologies.

2 Likes

But still, i’m sure roblox doesn’t moderate you for something exploiters themselves do right?

1 Like

That’s actually basically what this one is doing in the server :hidere:
But the animations they use are Roblox owned and have legitimate use cases for multiple games like slash animations. That’s basically what they’re doing, playing this animation in the TimePosition where it looks like this inappropriate animation.

This script simply just checks if these animations are being looped that way. :coefficients:

3 Likes

Ohhh alright, but still can’t the exploiter just create an animator, parent it to their character, and play it? since the server won’t know about it?

2 Likes

Maybe that’s possible, if they can play animations in their client that way.
But would probably defeat the whole purpose of making other clients see the same thing too.

3 Likes

But again, do games actually moderate you for something the exploiters did? I’ve seen several games with these sorts of exploiters, and none of their games have been moderated.

2 Likes

you know what i support this :slightly_smiling_face:

3 Likes

Indeed. because everything done on client can be tempered and will be eventually bypassed all it takes is just one person creating script that bypasses the client checks and entire client anticheat is basically useless doing server checks is just way better idea plus you would have more time working on actual content too

2 Likes

I completely agree with your view, the usage of this is skyrocketing in multiple games and I’ve seen them with my very own eyes. I wish Roblox could do something about this themselves so we didn’t have to worry about this.

Yeah figured that was ragebait hopefully :c, thanks though! :+1:

2 Likes

I don’t think playing a roblox animation regardless of how inappropriate it is would reflect their human behaviour it’s just an animation it’s not that deep its not like its an actual nsfw content being displayed to a young audience lol

1 Like