How to make a 'Never show again' button for a gui?

  1. What do you want to achieve? Keep it simple and clear!
    I want to make a gui button that when clicked, will never show the gui again. Even when rejoining the game. Im pretty sure this will involve using datastore.

  2. What is the issue? Include screenshots / videos if possible!
    I am not familiar with datastore and I have no idea how to code datastore scripts.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I have looked on the dev hub and I haven’t found any solutions.

Hope somebody can help :smile:

Seems complex for you if you do not even have a data store script.

Would the GUI be initiated on the client or the server?

It would be initiated on the client.

I see. In that case, you will want to use a RemoteEvent if the user clicks the “Never show again” button. Send it to the server, and the server will store the data. When the player joins the game again, the server can set an attribute in the player object.

At least that’s what I’m gathering. One possible issue is that the player might want to see the confirm dialog again after they’ve clicked “Never show again”. Do you plan to cover this?

Im not planning to have the players see the gui again. The gui is only for players who is on an unsupported device. It just tells the player that the device they are on is unsupported.

Heres what I have so far
(button script)

local rp = game:GetService('ReplicatedStorage')
local remote = rp.dontShowAgain

script.Parent.MouseButton1Click:Connect(function()
	remote:FireServer()
end)

(server script service script)

local rp = game:GetService('ReplicatedStorage')
local remote = rp.dontShowAgain

local player = game:GetService('Players')
local DS = game:GetService('DataStoreService')

remote.OnServerEvent:Connect(function()
	
end)

The only thing I really need help with is the datastore.

If you don’t want to use any Datastores then it will be a one season type fix only, no data is being saved so the game can’t know if it was pressed on a previous session, but you can make a table and have a variable to dictate if the Gui should show or not and then tell the client to add the Gui if the variable is true

How would I do that? I know your not really supposed to ask for full scripts but I have no idea how to do this. And I want to use datastore with this

I’m not the most knowledgeable about Datastores but you get a value in the datastore for the player, then you get it somewhere where the localPlayer has acces to, you can do this with RemoteFunction that returns the value or setting it on a BoolValue or any other value instance or attribute property, then tell the script that manages that Gui (if it has one) to just destroy (or hide) itself if the value is true

I’ve tested something myself and it works. I’ll try my best to explain the process i did and also link resources below in-case you would like to read more.

What i did:
I created a ScreenGui and only added the TextButton gui element as it’s child.
I renamed the TextButton to “DontShowAgainButton”.
I then added a LocalScript inside of the TextButton and added the following code:

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

local GuiElementsEvent = ReplicatedStorage:WaitForChild("GuiElements")

local localPlayer = Players.LocalPlayer
if not localPlayer then
	Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
	localPlayer = Players.LocalPlayer
end

local PlayerGui = localPlayer:FindFirstChildOfClass("PlayerGui") or localPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local DontShowAgainButton = ScreenGui:WaitForChild("DontShowAgainButton")

DontShowAgainButton.Activated:Connect(function()
	
	if localPlayer:GetAttribute("DisablePrompt") ~= true or nil then	
		GuiElementsEvent:FireServer("DontShowAgain", true)
	end
	
end)

What i’m doing with this code is i’m getting the ScreenGui from the PlayerGui itself and referencing the “DontShowAgainButton” on the ScreenGui.

Then i’m checking if the player has the attribute “DisablePrompt” and if it’s set to true to make sure the player didn’t already press the button and spam fire the event.

I then pass in 2 arguments, the first argument is “DontShowAgain” and the second argument is a boolean value which is true. I do this normally with my scripts and i normally limit the usage of a lot of RemoteEvents so i keep it organized by checking its first argument for it’s “request”.

I created a Script in ServerScriptService and for safety measures i set it’s RunContext to Server.
I then added the following code:

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

local dontShowAgainDataStore = DataStoreService:GetDataStore("PlayerData", "DontShowAgainGui")

local GuiElementsEvent = ReplicatedStorage.GuiElements

Players.PlayerAdded:Connect(function(player: Player)
	
	local playerUserId = "Player_" .. player.UserId
	local PlayerGui = player:FindFirstChildOfClass("PlayerGui") or player:WaitForChild("PlayerGui")
	local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
	
	local success, elementStatus = pcall(dontShowAgainDataStore.GetAsync, dontShowAgainDataStore, playerUserId)
	
	if success then
		
		if elementStatus == false then
			ScreenGui.Enabled = true
		
		elseif elementStatus == true then
			ScreenGui.Enabled = false
			ScreenGui:Destroy()
		end
		
	else
		player:Kick("Failed to load DataStore, please rejoin")
		return
	end
	
end)

GuiElementsEvent.OnServerEvent:Connect(function(player: Player, request: string, requestBool: boolean)
	if typeof(player) ~= "Instance" then return end
	if typeof(request) ~= "string" then return end
	if typeof(requestBool) ~= "boolean" then return end
	
	local playerUserId = "Player_" .. player.UserId
	local PlayerGui = player:FindFirstChildOfClass("PlayerGui") or player:WaitForChild("PlayerGui")
	local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
	
	if request == "DontShowAgain" and requestBool == true then
		
		local success, updatedStatus
		
		success, updatedStatus = pcall(dontShowAgainDataStore.SetAsync, dontShowAgainDataStore, playerUserId, true)
		
		if success == true then
			ScreenGui.Enabled = false
			ScreenGui:Destroy()
			
			player:SetAttribute("DisablePrompt", true)
			print("Successfully saved data for", player.Name)
			
		elseif success == false then
			repeat
				success, updatedStatus = pcall(dontShowAgainDataStore.SetAsync, dontShowAgainDataStore, playerUserId, true)
			until
			success == true
		end
		
	end
	
end)

What i’m doing here is i’m creating the DataStore called “PlayerData” with it’s scope “DontShowAgainGui”.

Then whenever a player is added, i would create the key which is the playerUserId variable. (It’s essential to set it as the players UserId as it’s easy to check that specific players data, it doesn’t change and it’s easy to identify and remove it in the case of a Right-To-Erasure request) I also referenced the ScreenGui that contains the prompt we want to remove. I then get the data from the scope (DontShowAgainGui) using a pcall and do some checks.

If the call ran successfully, we check the status/data of the player. If the call was unsuccessful, we would kick the player and return nothing to stop the function from running further. We kick the player to ensure their data loads successfully and doesn’t cause any issues.

If it ran successfully, the data we check will be a boolean value we sent from the RemoteEvent on the LocalScript earlier.

If the status is false, we would enable the ScreenGui for the player. If the status is true, we would disable the ScreenGui and destroy it from the PlayerGui to save memory and space.

For the OnServerEvent i passed in 2 extra arguments like the ones we sent to the server. A request which is a string and a requestBool which is a boolean value.

I then do some checks for those arguments as an extra layer of security to ensure the data that’s sent is correct using typeof.

After that i would create the playerUserId variable again and reference the ScreenGui from PlayerGui.

Then i’d check for the request and the requestBool to see if they match what’s being sent from the client. If they do match, it would save the players data for that scope as true, disable and destroy the ScreenGui, and set the players attribute to true to ensure the player can’t spam the event if they somehow still have the gui element.

If it wasn’t a success, it would try to repeatedly save until it’s a successful save.

Resources:
DataStore
DataStore Scopes
RemoteEvents
RemoteEvents and Callbacks
Client-Server Runtime
pcall (Protected Call)
typeof

I hope this helps and makes you understand a bit more on how you can do something like this. It’s not the best but that’s how i typically write my code and organize them, especially in a short amount of time. I tested it and it works and also the attribute is only in one game instance, it doesn’t save with the datastore.

Holy. Just saw this. Im gonna test it out now! Hope it works

Weren’t scopes deprecated? You’re supposed to use prefixes now.

Thank you so much for this solution!

1 Like

I did notice this. On line 60

success == true

‘Type false cannot be compared with true.’
This isn’t effecting the script but should probably be fixed

I have no idea if it would change anything but you can also just do if sucess then, does the same as if sucess==true,same works with false if you write if not sucess then

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.