Client cannot detect server changes

My client script does a wait function until an attribute changes its value which is through the server. The problem is that while the server changes the attribute’s value, and recognises the change, the client never does, for some reason.

Pls help!

Can you please provide the relevant server side and client side code, otherwise we can’t assist further.

alr but the code is really long

Send the relevant parts only then.
Make sure you enclose it in a codeblock.

i.e. (copy and paste the below)

```
– CODE GOES HERE
```

client

                    -- Function to run the clicked ability in the weapon
		local function onButtonActivated()
			-- Sends the function to the server via a remote event to run the ability with its settings
			ActivateAbility:FireServer(attack, weapon)

			-- Setting the cooldown screen and initates cooldown as a coroutine depending on the ability that's getting the cooldown
			local function abilitywait(ability, waitingTime)
				for i = waitingTime, 0, -0.1 do
					-- set the text of the cooldown screen to the current number rounded up to one decimal place
					ability.GuiCooldown.Value = i
					ability.Shadow.Cooldown.Text = math.ceil(ability.GuiCooldown.Value * 10) / 10 

					task.wait(0.1)

					-- If the existing cooldown is lower than the GuiCooldown value, then drop it to avoid overlapping
					if ability.GuiCooldown.Value ~= i then
						return
					end
				end

				-- Making the button usable after the cooldown
				ability.Active = true
				ability.Shadow.Visible = false
			end

			-- Handles the ability's cooldown as well as preventing other abilities to be run immediately
			for i, ability in pairs(button.Parent:GetChildren()) do
				if ability:IsA("ImageButton") then
					-- Disables all buttons after an ability has been activated
					ability.Active = false
					ability.Shadow.Visible = true

					local boolValueObject = attack:GetAttribute("HoldCooldown")

					local function startCooldown()
						-- Check which cooldown to apply
						if ability == button then
							coroutine.wrap(abilitywait)(ability, attackConfig["Cooldown"])
						elseif ability.GuiCooldown.Value < 2 then
							coroutine.wrap(abilitywait)(ability, 2)
						end
					end

					if boolValueObject == nil or boolValueObject == false then
						print(boolValueObject)
						startCooldown() -- Start cooldown immediately if no boolValue or it's already false
						continue
					end
					
					if ability == button then
						AddPlaceholderSentry(attack)
						
						-- If the boolValue exists and is true, wait until it's false before starting the cooldown
						coroutine.wrap(function()
							warn(attack.Parent.Parent.Parent)
							-- Wait until the boolValue is false
							while boolValueObject == true do
								RunService.Heartbeat:Wait()
							end
							startCooldown() -- Start the cooldown after the boolValue becomes false
						end)()
					end
					
				end
			end
		end

	if sentryToSpawn and input.UserInputType == Enum.UserInputType.MouseButton1 then
		SpawnSentry:FireServer(sentryToSpawn.Name, sentryToSpawn.PrimaryPart.CFrame)
		RemovePlaceholderSentry()
	end
end)

server

local function SendSentryInfo(player, name, cframe, weapon)
	-- Ability will actually place the tower, this is just sending info to the module script
	local value1 = Instance.new("StringValue")
	value1.Name = "NameValue"
	value1.Value = name
	value1.Parent = weapon

	local value2 = Instance.new("CFrameValue")
	value2.Name = "CFrameValue"
	value2.Value = cframe
	value2.Parent = weapon
	
	local config = nil
	
	-- Allow for client cooldown to start and ability script to place the sentry in the server
	for i, v in weapon.Abilities:GetChildren() do
		if v.Sentry.Value.Name ~= name then
			continue
		end
		
		config = v
	end
	
	config:SetAttribute("HoldCooldown", false)
	warn(config.Parent.Parent.Parent)
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		local backpack = player:WaitForChild("Backpack")

		backpack.ChildAdded:Connect(function(weapon)
			if not weapon:FindFirstChild("Config") then
				SpawnSentry.OnServerEvent:Connect(function(player, name, cframe)
					SendSentryInfo(player, name, cframe, weapon)
				end)
				return
			end		
		end)
	
	end)
end)

module script when the function is stored

["Gun Sentry"] = function(player, attack, config)
		print(player, attack, config)
		local humanoid = player.Character.Humanoid
		local weapon = attack.Parent.Parent

		humanoid.WalkSpeed = 0
		player.PlayerGui.CharacterGui.Abilities.Template.Modal = true

		while attack:GetAttribute("HoldCooldown") ~= false do
			wait()
		end
		
		warn(attack:GetAttribute("HoldCooldown"))

		local name = weapon:WaitForChild("NameValue")
		local cframe = weapon:WaitForChild("CFrameValue")

		print(name, cframe)

		local sentryExists = weapon.Sentries:FindFirstChild(name.Value)

		if not sentryExists then
			warn("Requested sentry does not exist")
			attack:SetAttribute("HoldCooldown", true)
			return
		end

		local newSentry = sentryExists:Clone()
		newSentry.HumanoidRootPart.CFrame = cframe.Value
		newSentry.Parent = workspace
		
		task.wait() -- the model doesnt actually appear it just fires unless if i add this wait 
		
		for i, v in newSentry:GetDescendants() do
			if not v:IsA("BasePart") then
				continue
			end

			v.Anchored = true
		end

		player.PlayerGui.CharacterGui.Abilities.Template.Modal = false
		humanoid.WalkSpeed = 25
		
--		task.wait(0.1)
		attack:SetAttribute("HoldCooldown", true)
	end,

Its all centred on the hold cooldown value

Are you using Instance:GetAttributeChangedSignal(AttributeName):Wait()?

nope, is it better to use???

The issue is this (a couple of lines were removed for clarity):

local boolValueObject = attack:GetAttribute("HoldCooldown")
while boolValueObject == true do
	RunService.Heartbeat:Wait()
end

You are only ever setting the value of boolValueObject once.

You should instead be doing

local boolValueObject = attack:GetAttribute("HoldCooldown")
while boolValueObject == true do
	RunService.Heartbeat:Wait()
	boolValueObject = attack:GetAttribute("HoldCooldown")
end

or

local boolValueObject = attack:GetAttribute("HoldCooldown")
while boolValueObject = attack:GetAttribute("HoldCooldown")== true do
	RunService.Heartbeat:Wait()
end

or, as suggested by sonic_848:

 attack:GetAttributeChangedSignal("HoldCooldown"):Wait()

ok I actually tried this properly and it works

it took me another hour to fully fix the cooldown to how I intended it to be as another problem had sprung up that wasn’t part of the problem I wrote about in the post

onButtonActivated function

-- Function to run the clicked ability in the weapon
		local function onButtonActivated()
			-- Sends the function to the server via a remote event to run the ability with its settings
			ActivateAbility:FireServer(attack, weapon)

			-- Setting the cooldown screen and initates cooldown as a coroutine depending on the ability that's getting the cooldown
			local function abilitywait(ability, cooldown)
				local waitingTime = nil
				ability.Shadow.Cooldown.Visible = true 
										
				-- Check if the button is the same as the activated button						
				if ability == button then
					waitingTime = cooldown
				else
					-- Start cooldown with a default value if not
					if ability.GuiCooldown.Value < 2 then
						waitingTime = 2
					end
				end	
				
				for i = waitingTime, 0, -0.1 do
					-- set the text of the cooldown screen to the current number rounded up to one decimal place
					ability.GuiCooldown.Value = i
					ability.Shadow.Cooldown.Text = math.ceil(ability.GuiCooldown.Value * 10) / 10 

					task.wait(0.1)

					-- If the existing cooldown is lower than the GuiCooldown value, then drop it to avoid overlapping
					if ability.GuiCooldown.Value ~= i then
						return
					end
				end

				-- Making the button usable after the cooldown
				ability.Active = true
				ability.Shadow.Visible = false
			end
			
			-- Handles the ability's cooldown as well as preventing other abilities to be run immediately
			for _, ability in pairs(button.Parent:GetChildren()) do
				-- Only process ImageButton abilities
				if not ability:IsA("ImageButton") then
					continue
				end

				-- Disable all ability buttons after activation to prevent immediate reactivation
				ability.Active = false
				ability.Shadow.Visible = true

				-- Check if there's a HoldCooldown attribute
				local boolValueObject = attack:GetAttribute("HoldCooldown")

				-- If no HoldCooldown or it is false, start cooldown immediately
				if boolValueObject == nil or boolValueObject == false then
					coroutine.wrap(abilitywait)(ability, attackConfig["Cooldown"])
				else
					-- If the boolValue exists and is true, wait until it's set to false before starting the cooldown
					ability.Shadow.Cooldown.Visible = false
					if ability == button then
						AddPlaceholderSentry(attack) -- Add placeholder sentry for this ability
					end

					-- Wait for HoldCooldown to change to false, then start cooldown
					coroutine.wrap(function()
						attack:GetAttributeChangedSignal("HoldCooldown"):Wait()
						abilitywait(ability, attackConfig["Cooldown"])
					end)()
				end
			end
			
		end

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