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.
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)
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 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)
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
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.
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.
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.