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