How to make this script not fire to all clients?

Hi Developers! :wave:

So I’ve been making weapon system for my game, when this big issue appeared: when client interacts with buttons, the event is fired not only for the player, but ALL clients present on the server. Does it have to do something with the for loop? And how to fix this?

I’d appreciate sany help provided. And if I need to clarify some info, let me know! :hidere:

Local script located in StarterGui

I don’t know what exact piece of code to put here…

local tweenService = game:GetService("TweenService")
local rs = game:GetService("ReplicatedStorage")

local getunlockedEvent = rs.WeaponSystemEvents:WaitForChild("GetUnlockStatus")
local getpriceEvent = rs.WeaponSystemEvents:WaitForChild("GetPrice")

local plr = game.Players.LocalPlayer

local weaponsFolder = rs:WaitForChild("WeaponStorage")
local weaponClasses = {weaponsFolder.Historical, weaponsFolder.Unnatural}
local weaponTypes = {weaponsFolder.Historical:GetChildren(), weaponsFolder.Unnatural:GetChildren()}


local frame = script.Parent
local loadout = frame:WaitForChild("Loadout")
local ActualUI = script.Parent.Parent.Parent.Parent.Parent:WaitForChild("ActualUI")

local currentMultiplierTable = {}


local function updateLoadout(primary, secondary, melee)
	print(primary, secondary, melee)
	
	frame.Parent.EquippedWeapons.WeaponsEquipped.Text = ""
	
	if primary ~= nil then
		frame.Parent.EquippedWeapons.WeaponsEquipped.Text = "Primary: " .. primary
	end
	
	if secondary ~= nil then
		frame.Parent.EquippedWeapons.WeaponsEquipped.Text = frame.Parent.EquippedWeapons.WeaponsEquipped.Text .. "\nSecondary: " .. secondary
	end
	
	if melee ~= nil then
		frame.Parent.EquippedWeapons.WeaponsEquipped.Text = frame.Parent.EquippedWeapons.WeaponsEquipped.Text .. "\nMelee: " .. melee
	end
end
updateLoadout(" ", " ", " ")




for i, weaponClass in ipairs(weaponClasses) do
	
	local newButton = script.WeaponClassButton:Clone()
	newButton.Text = weaponClass.Name
	
	newButton.Parent = loadout.WeaponClasses
	
	newButton.MouseButton1Click:Connect(function()
		
		
		for i, child in pairs(loadout.TypesList:GetChildren()) do

			if child:IsA("TextButton") then child:Destroy() end
		end
		
		for i, child in pairs(loadout.WeaponsList:GetChildren()) do

			if child:IsA("TextButton") then child:Destroy() end
		end

		
		for i, weaponType in ipairs(weaponClass:GetChildren()) do
			
			local newTypeButton = script.WeaponTypeButton:Clone()
			newTypeButton.Text = weaponType.Name

			newTypeButton.Parent = loadout.TypesList
			
			newTypeButton.MouseButton1Click:Connect(function()
				
				
				for i, child in pairs(loadout.WeaponsList:GetChildren()) do

					if child:IsA("TextButton") then child:Destroy() end
				end
				
				for i, weaponChild in ipairs(weaponType:GetChildren()) do

					local newWeaponButton = script.WeaponButton:Clone()
					newWeaponButton.Text = weaponChild.Name
					newWeaponButton.Name = weaponChild.Name
					newWeaponButton.Parent = loadout.WeaponsList
					
					print(weaponChild, weaponChild.ClassName)
					
					
					local statgui = frame.Parent.StatsFrame
					
					local UnlockedStatus = getunlockedEvent:InvokeServer(weaponChild.Name)
					if UnlockedStatus == nil then
						UnlockedStatus = false
					end
					print(UnlockedStatus, weaponChild.Name)

					newWeaponButton.MouseButton1Click:Connect(function()
						
						
						
						
						local SettingModule = require(weaponChild:WaitForChild("Setting"))
						local StatsModule = require(weaponChild:WaitForChild("Setting"):WaitForChild("1"))
						
						
						
						
						if UnlockedStatus then
							loadout.BuyDescFrame.BuyButton.Interactable = false
							loadout.BuyDescFrame.BuyButton.LockedUI.Visible = true
							loadout.BuyDescFrame.EquipButton.Interactable = true
							loadout.BuyDescFrame.EquipButton.LockedUI.Visible = false
						else
							loadout.BuyDescFrame.BuyButton.Interactable = true
							loadout.BuyDescFrame.BuyButton.LockedUI.Visible = false
							loadout.BuyDescFrame.EquipButton.Interactable = false
							loadout.BuyDescFrame.EquipButton.LockedUI.Visible = true
						end
						
						
						
						loadout.BuyDescFrame.WeaponNameLabel.Text = weaponChild.Name
						--add a description and an image preview for the weapon
						
						
						local damage = StatsModule.BaseDamage
						local firerate = StatsModule.FireRate
						local reload = StatsModule.ReloadTime
						local magcapacity = StatsModule.AmmoPerMag
						local spread = StatsModule.Spread
						local bulletspeed = StatsModule.BulletSpeed
						local walkspeed = SettingModule.WalkSpeed
						
						
						local basicstats = statgui.Container.StatTypeFrame.BasicStats
						local advancedstats = statgui.Container.StatTypeFrame.AdvancedStats
						
						basicstats.DamageLabel.Text = "Damage per bullet: " .. damage  --dmg
						basicstats.FireRateLabel.Text = "Fire rate: " .. firerate .."s" -- firerate
						basicstats.RldSpeedLabel.Text =	"Reload time: " .. reload .."s" -- reload
						basicstats.MagCapacityLabel.Text = "Mag capacity: " .. magcapacity  -- mag
						basicstats.SpreadLabel.Text = "Spread: " .. spread .. "°"  -- spread
						basicstats.BulletSpeedLabel.Text = "Bullet speed: " .. bulletspeed .." studs/s"  -- bullet speed
						basicstats.WalkSpeedLabel.Text = "Walk speed when equipped: " .. walkspeed .." studs/s" -- walk speed
						
						
						currentMultiplierTable = StatsModule.DamageMultipliers
						
						--headframe.MouseLeave:Connect(function() humhitFrame.LabelDesc.Text = "Hit multipliers:" end)
						
						
						
						--[[if weaponType.Name == "Primary" then
							primary = weaponChild
							script.Parent.StrPrimary.Value = weaponChild.Name	

						elseif weaponType.Name == "Secondary" then
							secondary = weaponChild
							script.Parent.StrSecondary.Value = weaponChild.Name					

						elseif weaponType.Name == "Melee" then
							melee = weaponChild
							script.Parent.StrMelee.Value = weaponChild.Name
							
						end]]
						
						
					end)
					
					if UnlockedStatus then
						loadout.WeaponsList[weaponChild.Name].LockedUI.Visible = false
					end
					
					--update UI if weapon has been bought
					
					rs.WeaponSystemEvents.UpdateWeapon.OnClientEvent:Connect(function(weaponName)
						if weaponName == weaponChild.Name then
							loadout.BuyDescFrame.BuyButton.Interactable = false
							loadout.BuyDescFrame.BuyButton.LockedUI.Visible = true
							loadout.BuyDescFrame.EquipButton.Interactable = true
							loadout.BuyDescFrame.EquipButton.LockedUI.Visible = false
							
							loadout.WeaponsList[weaponName].LockedUI.Visible = false
						end
					end)
					
					
					
					
					
					--indicating hit multipliers, it should be there because it wont work anywhere else :(

					local humhitFrame = statgui.Parent.AdvancedPlus.HumHitMap

					local headframe = humhitFrame.HeadFrame
					local torsoframe = humhitFrame.TorsoFrame
					local rightarmframe = humhitFrame.RightArmFrame
					local leftarmframe = humhitFrame.LeftArmFrame
					local rightlegframe = humhitFrame.RightLegFrame
					local leftlegframe = humhitFrame.LeftLegFrame	
					
					local function update(partName)
						humhitFrame.LabelDesc.Text = "Hit multiplier: " .. (currentMultiplierTable[partName] or "1")
					end
					
					headframe.MouseEnter:Connect(function() update("Head") end)--humhitFrame.LabelDesc.Text = "Hit multiplier: " .. headmulti

					torsoframe.MouseEnter:Connect(function()update("Torso") end)

					rightarmframe.MouseEnter:Connect(function()update("Right Arm") end)

					leftarmframe.MouseEnter:Connect(function()update("Left Arm") end)

					rightlegframe.MouseEnter:Connect(function()update("Right Leg") end)

					leftlegframe.MouseEnter:Connect(function()update("Left Leg") end)
					
					--==================buy and equip events fire=====================
					local buyEvent = rs.WeaponSystemEvents.Buy
					local equipEvent = rs.WeaponSystemEvents.Equip
					
					local Price = getpriceEvent:InvokeServer(weaponChild.Name)
					print(Price, "price for", weaponChild.Name, UnlockedStatus)
					
					local function ClosePrompt()
						ActualUI.BuyPrompt.Visible = false
					end
					
					loadout.BuyDescFrame.BuyButton.MouseButton1Click:Connect(function() 
						ActualUI.BuyPrompt.Visible = true
						ActualUI.BuyPrompt.QuestionLabel.Text = "Are you sure you want to buy this " .. weaponChild.Name .. " for:"
						ActualUI.BuyPrompt.PriceLabel.Text = Price .. "CC?"
					end)
					
					ActualUI.BuyPrompt.Cancel.MouseButton1Click:Connect(function() ClosePrompt() end)
					ActualUI.BuyPrompt.Close.MouseButton1Click:Connect(function() ClosePrompt() end)

					ActualUI.BuyPrompt.OK.MouseButton1Click:Connect(function() buyEvent:FireServer(weaponChild.Name) end)
					
					
					loadout.BuyDescFrame.EquipButton.MouseButton1Click:Connect(function() 
						equipEvent:FireServer(weaponChild.Name) 
						
						
						
						if weaponType.Name == "Primary" then
							updateLoadout(weaponChild.Name, nil, nil) 
						elseif weaponType.Name == "Secondary" then
							updateLoadout(nil, weaponChild.Name, nil) 
						elseif weaponType.Name == "Melee" then
							updateLoadout(nil, nil, weaponChild.Name) 
						end
					end)
				end
			end)	
		end
	end)
end


rs.WeaponSystemEvents.GetEquipped.OnClientEvent:Connect(function(t)
	local primary = nil
	local secondary = nil
	local melee = nil

	for wpntype, wpn in pairs(t) do
		print(type, wpn, "was equipped previously")
		if wpn then
			if wpntype == "Primary" then
				primary = wpn
			elseif wpntype == "Secondary" then
				secondary = wpn
			elseif wpntype == "Melee" then
				secondary = wpn
			end
		end
	end
	task.wait(0.5)
	
	updateLoadout(primary, secondary, melee) 
end)
3 Likes

Most likely, you’re firing a RemoteEvent from the client (using .FireServer()), and the server then broadcasts to all clients using .FireAllClients() or .FireClient() (in a loop to all players). That’s what causes every client to receive the effect. This cannot happen from LocalScripts alone.

2 Likes

like @2112Jay said, the issue lies probably in the serverscript that handles the remote events. Can we see your serverscript so we can help you further?

I’ve had a little skim over, and overall the issue seems to be a server script but there are some issues that I can see with the way you connect :OnClientEvent for every button inside the loops. Which can lead to duplicate connections.

Everytime you a weapon button is created, you create a new connection:

rs.WeaponSystemEvents.UpdateWeapon.OnClientEvent:Connect(function(weaponName)
	--code here
end)

This connection is created inside the for loop that creates weapon buttons.

So if you have 10 weapons buttons and open the menu one time: 10 connections will be made

All of those fire whenever the event is triggered across all clients who opened the menu.

So I would just move the connection out side of the loop.

The more probable issue is with your server script and how it’s firing to the players, is it using .FireAllClients() ? or Alternatively is it using .FireClient(player) inside a for loop. looping through the players?

for _, plr in player:GetPlayers() do 
    remoteevent:FireClient(plr, ...)
end

-- or 

RemoteEvent:FireAllClients()

You can use Ctrl + F and then search for the Remote event to find the connection easier :grinning:

The issue is only on client side, server side does not have any for loops or anything like that. These FireServer events are in the for loop, but how do I make them fire outside of that for loop or something?

I am a tad bit confused on what you mean, you mention that on the server side there are not any loops in general. Please could you provide the server code attached to the remote event so I can have a proper look. Also I’ve tried looking into your client code provided, but it doesn’t seem to be any FireServer() events, Only OnClientEvents which is fine.

Within your server code find:

rs.WeaponSystemEvents.GetEquipped:FireClients/FireClient ()

Any server script can be set up to pick up that remote.. What IakaR said here is golden for debugging things like this.

Correction: I meant to say.. Ctrl + Shift + F

Oh I’m sorry, there are InvokeServer and 2 FireServer events, but they work almost the same as FireServer.

As you said, here is the server script part with FireClient and

local updateEvent = rs.WeaponSystemEvents:WaitForChild("UpdateWeapon")
	local warning = rs.WeaponSystemEvents:WaitForChild("NotEnough")
	
	local function OnBuyEvent(wpn)
		warn(wpn, weaponPrices, weaponPrices[wpn]["price"])
		local price = weaponPrices[wpn]["price"]
		
		print("the price is", price, "weapon:", wpn, wpn.ClassName)
		
		if price and price ~= nil then
			if CC.Value >= price then
				CC.Value -= price
				GetWeapon(wpn)
				updateEvent:FireClient(player, wpn)
				
			else
				local needed = price - CC.Value
				
				warning:FireClient(player, needed)
			end
		end
	end


rs.WeaponSystemEvents.GetEquipped:FireClient(player, weaponTable.EquippedWeapons)

Hmm, your server code seems alright…not too sure. I do have some thoughts that you can look into, not too sure if they’ll work:

  1. It seems like the local script is a child of the gui button. I do not recommend this and try to put the local script within starterplayerscripts/startercharacterscripts or if thats complicated just simply move it out of the hierarchy

  2. I would like to see the InvokeSever call both client and server side please… if possible.

Here’s the server:

local giveunlockedEvent = rs.WeaponSystemEvents:WaitForChild("GetUnlockStatus")
	local givepriceEvent = rs.WeaponSystemEvents:WaitForChild("GetPrice")
	

	--"unlocked"
	giveunlockedEvent.OnServerInvoke = function(player, weaponName)
		
		for key, wpnData in pairs(weaponTable.OwnedWeapons) do
			warn(key)
			if weaponName == key then
				if wpnData then
					print("success, sent", key, wpnData, "to", player)
					return wpnData
				end
			end
		end
	end
	
	--price
	givepriceEvent.OnServerInvoke = function(player, weaponName)
		for i, priceData in pairs(weaponPrices) do
			if weaponName == priceData.name then
				print("success, sent prices(", priceData.price, ") to", player)
				return priceData.price
			end
		end
	end

And here’s the client (not the full function bcs it’s big):

for i, weaponChild in ipairs(weaponType:GetChildren()) do

					local newWeaponButton = script.WeaponButton:Clone()
					newWeaponButton.Text = weaponChild.Name
					newWeaponButton.Name = weaponChild.Name
					newWeaponButton.Parent = loadout.WeaponsList
					
					print(weaponChild, weaponChild.ClassName)
					
					
					local statgui = frame.Parent.StatsFrame
					
					local UnlockedStatus = getunlockedEvent:InvokeServer(weaponChild.Name)

--The second one
                    local Price = getpriceEvent:InvokeServer(weaponChild.Name)

Sorry for the late reply and unfortunately I’m still unable to verify the issue.

May I Just ask how you are referencing the player in your server script when you are firing to the client?

updateEvent:FireClient(player, wpn) -- how are you accessing player on the server? 

And print the player’s name right before you fire to the client (on the server) and then AFTER you fire to the client (inside the onclient event in the client script. Then afterwards show me what the results were when testing with 2 players.

print(player.."has gotten their weapon and now firing update event") 
print(player.."has been fired from the server via updateEvent")