Solve this and I will give you a kind donation

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:


ServerScript:

2 Likes

I’ve just got a couple of questions.

  1. Where are both of these scripts located?
  2. 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.
  3. 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.

3 Likes

triple backticks.
like this:
```
-- code
```
which will display:

-- code
1 Like

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.

1 Like

which they do have a large code block…?

1 Like

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!

So just put these ``` in front of each line of code? Sorry, I am unfamiliar with LUA all around haha

1 Like

Are your frames all in 1 location? Or do you want every single frame to be saved

so there is one universal attribute FrameVisible for every frame? is it for a mainframe?
i don’t think i understand your code

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