Jumping/stamina script

hello all, below I have a script for a stamina system, with a sort of recovery wait time for recharging stamina. The only problem I am facing now is the extreme inconsistency with the jumps. For some background info, this is all in a module script that gets required, and every system in it works fine, all the countdowns etc. work to a dime. The problem I am facing is, because I am putting this all in a module script, I have a feeling the reason this isn’t working is because of all the threads running at once. But I can’t be sure because even when testing it by itself the jumping is super inconsistent but I could be wrong.

https://www.roblox.com/games/10076876312/forum-Test

below is the link to the game, If i’m able to figure out how to make it able to be viewed/copied by everyone I can do that but you can go into the game and test it for yourself, you just might have to jump 10 times before it starts counting your jumps. I have tried many variants of this and nothing seems to be a fix all solution. I am also wondering whether having it set entirely in a server script is realistic or not, because while it should solve a lot of exploits it’s causing more issues and I’m wondering if its really necessary or not. Any help is much appreciated, thank you

local staminaScript = {}

local Players = game:GetService("Players")

local maxStaminaValue = 100
local takeStaminaValue = 25
local recoverStaminaValue = 5
local maxRecoveryValue = 5
local jumpDebounce = 1

local function recoveryLoop(player)
	print("recoveryLoop")
	if player then
		if player.recoveryTimer.Value > 0 and player.recovering then
		repeat 
				player.recoveryTimer.Value -= 1
				wait(1)
		until player.recoveryTimer.Value == 0
			player:WaitForChild("recovering").Value = false
		end
	end
end

local function takeStamina(player)
	print("takeStamina")
	if player then
		if player.stamina.Value >= takeStaminaValue then
			player:WaitForChild("stamina").Value -= takeStaminaValue
			print(player.stamina.Value) 
			end
		player:WaitForChild("recoveryTimer").Value = maxRecoveryValue
		if not player.recovering then
			player.recovering.Value = true
		else
			recoveryLoop(player)
		end
	else 
		return	
	end
end

local function setJumpPower(player)
	player.stamina.Changed:Connect(function()
		print("staminaChanged")
		if player.stamina.Value < takeStaminaValue then
			player.Character:WaitForChild("Humanoid").JumpPower = 0
		else
			player.Character:WaitForChild("Humanoid").JumpPower = 50
		end
		
	end)
end

local function recoverStamina(player)
	if player:WaitForChild("recovering") then
		while wait(.5) do
			if not player.recovering.Value then 
				print("recovering stamina")
				if player.stamina.Value < maxStaminaValue then
					print(player.stamina.Value)
					player.stamina.Value += recoverStaminaValue
				end
			end
		end
	end
end

local function playerJumped(player, oldState, newState)
	local isJumping = player:WaitForChild("isJumping")
	local char = player.Character or player.CharacterAdded:Wait()
	local humanoid = char:WaitForChild("Humanoid")
	if newState == Enum.HumanoidStateType.Jumping then
		if not isJumping.Value then
			print("playerJumped")
			isJumping.Value = true
			humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
			player:WaitForChild("recovering").Value = true
			takeStamina(player)	
			print("playerJumpedp2")
		end
	elseif newState == Enum.HumanoidStateType.Landed then
		print("playerJumped/landed")
		if isJumping.Value then
			isJumping.Value = false
			wait(jumpDebounce)
			humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true)
		end
	end
end

local function func(player)
	print("func")
	local stamina = Instance.new("IntValue")
	stamina.Value = maxStaminaValue
	stamina.Name = "stamina"
	stamina.Parent = player
	local recoveryTimer = Instance.new("IntValue")
	recoveryTimer.Value = 5
	recoveryTimer.Name = "recoveryTimer"
	recoveryTimer.Parent = player
	local recovering = Instance.new("BoolValue")
	recovering.Value = true
	recovering.Name = "recovering"
	recovering.Parent = player
	local isJumping = Instance.new("BoolValue")
	isJumping.Value = false
	isJumping.Name = "isJumping"
	isJumping.Parent = player
	
	local staminacoroutine = coroutine.create(recoverStamina)
	coroutine.resume(staminacoroutine,player)
	local jumpPowercoroutine = coroutine.create(setJumpPower)
	coroutine.resume(jumpPowercoroutine,player)
	recoveryLoop(player)
	
	local char = player.Character or player.CharacterAdded:Wait()
	char:WaitForChild("Humanoid").UseJumpPower = true
	
	char:WaitForChild("Humanoid").StateChanged:Connect(function(oldState,newState)
		playerJumped(player,oldState,newState)
	end)
end

Players.PlayerAdded:Connect(func)

return staminaScript