Counting jumps on server-side script

I have noticed multiple instances where a script thats for a humanoid works perfectly fine locally, but can’t be done through a server script. This always happens whenever its detecting/affecting properties of a humanoid.

but the reason I’m writing this topic and the specific issue that I’m facing is that I’m developing a game where there is a “Jumps” stat, I wrote a simple script to count jumps that works perfectly fine locally. this is it below.

humanoid.StateChanged:Connect(function(old, new)
    if new == Enum.HumanoidStateType.Jumping then
        LocalPlayer.leaderstats.Jumps.Value += 1
    end
end)

and here is what the code does. it works perfectly as intended.

however, when I tweak the script and put it into a server script. this is what happens.

if there is absolutely no way to make this script work on the server, I’m wondering what the most optimal way would be to constantly transfer multiple leaderstats from client to server in real time.

my first thought was to simply use remoteEvents and fire it everytime the stat is changed, however I want to try to stay away from them, firstly because I don’t want to risk any exploiters being able to fire them (idk much about implementing anti-cheat), and secondly because it seems quite complicated just for some simple leaderstats, and I will end up having lots of leaderstats so I dont want any type of lag or stat glitches.

please share with me if anybody knows how this could be controlled server-sided, or if it does require remoteEvents, what the most optimal and safe way to do it would be.

thank you.

1 Like

Is this in StarterPlayerScripts or StarterCharacterScripts?

the local script is in startercharacter

Could you provide the full script?

local Players = game:GetService("Players")
local LocalPlayer = Players.LocalPlayer
local character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")

humanoid.StateChanged:Connect(function(oldState, newState)
    if newState == Enum.HumanoidStateType.Jumping then
        LocalPlayer.leaderstats.Jumps.Value += 1
    end
end)

here is the server version of the script

game.Players.PlayerAdded:Connect(function(plr)
	local character = plr.Character or plr.CharacterAdded:Wait()
	local humanoid = character:WaitForChild("Humanoid")

	humanoid.StateChanged:Connect(function(old, new)
		if new == Enum.HumanoidStateType.Jumping then
			plr.leaderstats.Jumps.Value += 1
		end
	end)
end)

I mean you can use plr.CharacterAdded:Connect(function(character) instead of doing wait. Not sure if it’ll fix it but worth a shot

true thats a better way to write it, but it doesn’t fix my issue sadly

try this out

game.Players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(character)
		local humanoid : Humanoid = character:WaitForChild("Humanoid")

		humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(function()
			if humanoid.FloorMaterial == Enum.Material.Air then
				plr.leaderstats.Jumps.Value += 1
			end
		end)
	end)
end)

I think I have fixed your script, I edited it a bit so that it will detect even if you reset.

local function setupJumps(player)
	local character = player.Character or player.CharacterAdded:Wait()
	local humanoid = character:FindFirstChildOfClass("Humanoid")
	
	if humanoid then
		humanoid.StateChanged:Connect(function(oldState, newState)
			if newState == Enum.HumanoidStateType.Freefall then
				player.leaderstats.Jumps.Value += 1
			end
		end)
		
		player.CharacterAdded:Connect(function()
			setupJumps(player)
			return
		end)
	end
end

game.Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local jumps = Instance.new("NumberValue")
	jumps.Name = "Jumps"
	jumps.Parent = leaderstats
	
	setupJumps(player)
end)

that is a good idea but it gets added with a delay, and also isn’t reliable for jumping under surfaces.

that is way more reliable but the only problem is when I jump under a part it skips alot of jumps.

Well, HumanoidState is really unreliable on the server, I would make a RemoteEvent, add a client script and have the server detect when it’s fired.

game.Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local jumps = Instance.new("NumberValue")
	jumps.Name = "Jumps"
	jumps.Parent = leaderstats
	
	game.ReplicatedStorage:WaitForChild("JumpEvent").OnServerEvent:Connect(function(eventPlayer)
		if eventPlayer == player then
			player.leaderstats.Jumps.Value += 1
		end
	end)
end)
local Players = game:GetService("Players")
local LocalPlayer = Players.LocalPlayer

local character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")

local event = game.ReplicatedStorage:WaitForChild("JumpEvent")

humanoid.StateChanged:Connect(function(oldState, newState)
	if newState == Enum.HumanoidStateType.Jumping then
		event:FireServer()
	end
end)

No one in their minds would do that, exploiters would just spam the remote to increase jumps.
And the fact that why they will get more and more jumps on the event fire with alot of people in the server cause you connect it in PlayerAdded

3 Likes

They would not get more and more jumps, it only use PlayerAdded to tell if the player sending the remote is the connected player. The only way I could think to stop exploiters from spamming the remote is by adding a debounce, but people could spam jump anyways under a short roof.

I want people to be able to spam jump under a short roof, thats the main problem with your first script.

1 Like

Oh the good ol’ tale of trying to detect when a player jumps on the server-side…

I had a (albeit a little incomplete) venture into this, and I can conclude that the most reliable way should be to simply use a RemoteEvent which fires when the client detects a jump via ContextActionService:BindAction with Enum.PlayerActions.CharacterJump (actually now that I look into that it seems to be deprecated… Humanoid.Jumping it is then I guess?)

Here’s my, rather incomplete, spreadsheet that I did some tests for.

You clearly don’t know how events and remotes work.

What’s the reason for multiple signals on server? I mean the OP mentions unreliability yet you provide piece of code that is bad and unreliable.

2 Likes

Exploiters can already manipulate their humanoid in weird ways. I imagine even if you didn’t use remotes, they could just manipulate their humanoid to make the server detect jumps as fast as possible, much faster than any other player. There isn’t much you can do with exploiters regarding that, since jumping is reliant on player input.

Best bet would be to just rate-limit how fast a player can get jumps.

And you are forgetting that jumping is physics, client owns the character and it’s physics therefore it replicates to server