I have no idea if this is allowed on the forums, but I am sick and tired of trying over and over and having no success. I am offering a 5K robux donation to whoever can give me a solution to this problem. Long story short, the visibility of frames in my game changes and I want to place a LocalScript under these many frames. I want it to save the state of all of the frames’ visibilities when a player leaves and when the player rejoins the state of visibility remains as it was when the player left. Furthermore, there are many frames with the same name that will have this LocalScript, so I need this script to assign each frame an ID so changes are unique to each frame. I am going to provide the LocalScript and the ServerScript it correlates with below. If anyone is intrigued in helping and has any questions feel free to ask. As I said, I am willing to provide a donation if it gets me the solution I need.
LocalScript:
Are you sure by the time you use FireClient in data loading, the client has registered a callback for it? Remember the client takes time to load.
Is the data saving fully? Do you have a BindToClose function? Closing in studio will close the current server and you cannot be sure data has fully saved, as it is an asynchronous function call.
(Also, please format your code using backticks (`). It makes it easier to read because we can scroll through it :D)
You could try replacing the OnClientEvent callback with a simple task.spawn(updateVisibility), since that will read the attribute anyway.
Depends, really. If you have a large code block, then triple backticks are better, but for a short and simple phrase it is not really worth making a new line of code for it. That’s why I just said backticks.
I’m going to be honest, I am a builder that was unable to find a scripter for my game so I have been winging it with ChatGPT haha. Yes, I know, it can be bad at times, but it has helped with a decent amount. With that said, the LocalScript is located under a Frame and it is pasted under many other Frames. The ServerScript is pasted under ServerScriptService. I am not sure in regards to your second question, could we add a print statement to figure that out? The data saves somewhat. For instance, when a frame’s visibility changes, it saves, but the problem is that all of the other frames containing the LocalScript also adjust to the visibility of the frame that changed. I apologize if I am asking any stupid questions. I appreciate the help!
The client takes time to load, and depending on how long it takes to load, it might not have been able to run the code that connects the OnClientEvent to your given function. Try replacing it with a task.spawn instead of an OnClientEvent.
As for data saving, the BindToClose runs any code given to it before the game closes. We can utilise this to help with data saving. You mentioned saving for mass frames, in which you would use a table with UpdateAsync, but I’ll demonstrate with one.
local players = game:GetService("Players")
local runService = game:GetService("RunService")
--in place of your game.Players.PlayerAdded, etc.
local function save(player:Player)
local isVisible = player:GetAttribute("FrameVisible")
local success, result
local attempt = 0
repeat
success, result = pcall(frameVisibilityStore.SetAsync, frameVisibilityStore, "FrameVisibility"..player.UserId, isVisible)
attempt += 1
until
success or attempt == 3
print(success, result)
end
local function load(player:Player)
--your loading code goes here. Just set the attributes after loading data.
end
players.PlayerAdded:Connect(load)
players.PlayerRemoving:Connect(save)
game:BindToClose(function() --force the game to wait so data requests can go through
if runService:IsStudio() then task.wait(5) return nil end
for _, player in next, players:GetPlayers(), nil do
save(player)
end
task.wait(5)
end)
If that doesn’t work, can you try adding some print statements after data loading (printing the data), data saving, and on both sides of the remote, and let me know what outputs?
Rewrote some of your code & placed some comments, there’s more to datastores but this will do the job for this case, I hope this helps
-- ServerScriptService/Script
local DataStoreService = game.DataStoreService
local ReplicatedStorage = game.ReplicatedStorage
local Players = game.Players
local FrameVisibilities = DataStoreService:GetDataStore('FrameVisibilityStore')
local Remote = Instance.new('RemoteEvent') -- Create the Remote
Remote.Name = 'UpdateVisibilityEvent'
Remote.Parent = ReplicatedStorage
Remote.OnServerEvent:Connect(function(LocalPlayer, val)
if type(val) ~= 'boolean' then
return
end
LocalPlayer:SetAttribute('FrameVisible', val) -- When the remote is fired, set the FrameVisible attribute to the value
end)
local Save = function(LocalPlayer, Visible)
local Success
local Attempts = 0
repeat
Success = pcall(function()
FrameVisibilities:SetAsync(`FrameVisibility_{tostring(LocalPlayer.UserId)}`, Visible) -- Save the data
end)
Attempts += 1
task.wait(0.2)
until Success or Attempts >= 3
if not Success then
LocalPlayer:Kick('Fatal data save error')
return
end
print(`Data saved in {Attempts} attempts, {Visible}`)
end
Players.PlayerAdded:Connect(function(LocalPlayer)
local Success
local Visible
local Attempts = 0
repeat
Success, Visible = pcall(function()
return FrameVisibilities:GetAsync(`FrameVisibility_{tostring(LocalPlayer.UserId)}`) -- Get the data
end)
Attempts += 1
task.wait(0.2)
until Success or Attempts >= 3
if not Success then
LocalPlayer:Kick('Fatal data load error')
return
end
print(`Data loadedd in {Attempts} attempts, {Visible}`)
if Success and Visible ~= nil then
LocalPlayer:SetAttribute('FrameVisible', Visible) -- Load the data
return
end
LocalPlayer:SetAttribute('FrameVisible', true)
end)
Players.PlayerRemoving:Connect(function(LocalPlayer)
Save(LocalPlayer, LocalPlayer:GetAttribute('FrameVisible')) -- Save data when LocalPlayer leaves
end)
game:BindToClose(function()
for _, LocalPlayer in Players:GetPlayers() do
Save(LocalPlayer, LocalPlayer:GetAttribute('FrameVisible')) -- Save the data for all players
end
end)
coroutine.wrap(function()
while true do
task.wait(30)
for _, LocalPlayer in Players:GetPlayers() do
Save(LocalPlayer, LocalPlayer:GetAttribute('FrameVisible')) -- Save the data every ~ 30 seconds
end
end
end)()
-- StarterGui/ScreenGui/Frame/LocalScript
local Players = game.Players
local ReplicatedStorage = game.ReplicatedStorage
local LocalPlayer = Players.LocalPlayer
local Frame = script.Parent
local Remote = ReplicatedStorage:WaitForChild('UpdateVisibilityEvent', math.huge) -- Remote
local UpdateVisibility = function()
local Visible = Frame.Visible
Remote:FireServer(Visible)
LocalPlayer:SetAttribute('FrameVisible', Visible)
end
LocalPlayer:GetAttributeChangedSignal('FrameVisible'):Connect(function()
Frame.Visible = LocalPlayer:GetAttribute('FrameVisible') -- Set Frame.Visible to the FrameVisible attribute
Frame:GetPropertyChangedSignal('Visible'):Once(UpdateVisibility)
end)
Frame.Visible = LocalPlayer:GetAttribute('FrameVisible')
Frame:GetPropertyChangedSignal('Visible'):Once(UpdateVisibility)