How can you deflect server updates on attributes?

Im making a gui based Undertale game, and I need to change attributes in the client and send them to the server. The problem is that, when testing in a local server in studio, it doesnt seem to have any problems, but when I move to roblox instances, the attributes seem to rubberband. I think it may be because in a local server theres no ping between client and server, so theres no time for the client to modify an attribute before the server updates it for the player, but in a roblox instance, with ping, the client can change an attribute before the server sends the updated attribute to the players, causing this effect. Can any of you guys help me?
I searched in google for this issue but didnt find much (I probably didnt search really well so if you got any links it would be helpful).
These are the scripts:

Client script

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

local player = Players.LocalPlayer
local remoteEvent = ReplicatedStorage:WaitForChild("UpdateValue")

local realTimeAttributes = {
	"SoulX",
	"SoulY",
	"HP"
}

local function isRealTimeAttribute(attributeName)
	for _, v in ipairs(realTimeAttributes) do
		if attributeName == v then
			return true
		end
	end
	return false
end

local lastSentValues = {}

local function sendUpdate(attributeName, newValue)
	if isRealTimeAttribute(attributeName) then
		remoteEvent:FireServer(attributeName, newValue)
		return
	end

	if lastSentValues[attributeName] ~= newValue then
		remoteEvent:FireServer(attributeName, newValue)
		lastSentValues[attributeName] = newValue
	end
end

player.AttributeChanged:Connect(function(attributeName)
	sendUpdate(attributeName, player:GetAttribute(attributeName))
end)

local SaveItems = player:FindFirstChild("SaveItems")
if not SaveItems then
	SaveItems = Instance.new("Folder")
	SaveItems.Name = "SaveItems"
	SaveItems.Parent = player
end

SaveItems.ChildAdded:Connect(function(child)
	remoteEvent:FireServer("AddItem", child.Name)
end)

SaveItems.ChildRemoved:Connect(function(child)
	remoteEvent:FireServer("RemoveItem", child.Name)
end)

RunService.RenderStepped:Connect(function()
	for _, attributeName in ipairs(realTimeAttributes) do
		local value = player:GetAttribute(attributeName)
		if value then
			sendUpdate(attributeName, value)
		end
	end
end)

and Server Script

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

local remoteEvent = ReplicatedStorage:WaitForChild("UpdateValue")

local rateLimits = {}

local excludedValues = {
	"CheatTimes"
}

local noCooldownValues = {
	"HP",
	"BattleMenu",
	"MRN",
	"UISelectionMenu",
	"Damaged",
	"BLUE_Side"
}

remoteEvent.OnServerEvent:Connect(function(player, attributeName, newValue)
	-- Verifica si el atributo está excluido (intento de exploit)
	if table.find(excludedValues, attributeName) then
		local cheatTimes = player:GetAttribute("CheatTimes") or 0
		player:SetAttribute("CheatTimes", cheatTimes + 1)

		if player:GetAttribute("CheatTimes") >= 3 then
			local duration = 3600
			local config = {
				UserIds = {player.UserId},
				Duration = duration,
				DisplayReason = "Exploits",
				PrivateReason = "The user tried to modify excluded attribute: " .. attributeName,
				ExcludeAltAccounts = false,
				ApplyToUniverse = true
			}

			local success, err = pcall(function()
				return Players:BanAsync(config)
			end)
			print(success, err)
		end
		return
	end

	if attributeName == "AddItem" or attributeName == "RemoveItem" then
		local SaveItems = player:FindFirstChild("SaveItems")
		if not SaveItems then return end

		if attributeName == "AddItem" then
			local newItem = Instance.new("StringValue")
			newItem.Name = newValue
			newItem.Parent = SaveItems
		elseif attributeName == "RemoveItem" then
			local item = SaveItems:FindFirstChild(newValue)
			if item then
				item:Destroy()
			end
		end
		return
	end

	if table.find(noCooldownValues, attributeName) then
		player:SetAttribute(attributeName, newValue)
		return
	end

	local currentTime = tick()
	if not rateLimits[player] then
		rateLimits[player] = {}
	end

	local lastUpdate = rateLimits[player][attributeName] or 0

	if (currentTime - lastUpdate) >= (1 / 30) then
		player:SetAttribute(attributeName, newValue)
		rateLimits[player][attributeName] = currentTime
	end
end)

change the attribute on the client

then from this client send a remote event to the server

on the server send a remote event to every other client to update the value

--first client
example:SetAttribute("example", 5)
remoteEvent:FireServer()
--server
remoteEvent.onServerEvent:Connect(function(sender)

  for _, client in ipairs(game.Players:GetPlayers()) do
    if client == sender then continue end
    remoteEvent:FireClient(client)
  end

end)
--on all other clients
remoteEvent.onClientEvent:Connect(function()
  example:SetAttribute("example", 5)
end

but i also need the server to have the value

also i can manage the attributes in the server, but then the movements are linked to the ping, so it looks really bad