Gun system causes client lag

The lag only occurs when the player is firing. I’m trying to get other opinions about what might be causing the lag to occur and how to possibly correct it.

Client Code (Under StarterPlayerScripts)

--//SERVICES//
local Players = game:GetService('Players')
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local UserInputService = game:GetService('UserInputService')

--//VARIABLES//
local EVENTS = ReplicatedStorage['EVENTS']
local MODULES = ReplicatedStorage['MODULES']
local RENDER = EVENTS:WaitForChild('RENDER')
local GUNMOD = require(ReplicatedStorage.MODULES:WaitForChild('WEAPON'))

local LocalPlayer = Players.LocalPlayer
local Character = nil
local Humanoid = nil

local ingoreList = {}

local loadedAnimations = {}
local registeredWeapons = {}

local equippedWeapon = nil
local Equipped = false
local equippedWeaponInfo = {}
local equippedWeaponAnimations = {}
local equippedWeaponRegister = {}
local mouseDown = false
local canFire = true
local isFiring = false
local Reloading = false
local isHolstering = false

local weaponGUI = nil
local sprintBtn = nil
local crouchBtn = nil
local reloadBtn = nil

local processedFirstChar = false

local redIcon = 'rbxassetid://131718495'
local whiteIcon = 'rbxassetid://131581677'
local greenIcon = 'rbxassetid://131718487'

local crouchLoop
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------

--//FUNCTIONS
local function tk(plr, targetPlayer)
	if plr.Team == game.Teams.Astria then
		if targetPlayer.Team == game.Teams.Astria then
			if plr:GetRankInGroup(3167990) < 80 then
				return true
			else
				return false
			end
		else
			return false
		end
	elseif plr.Team ~= game.Teams.Astria then
		if targetPlayer.Team ~= game.Teams.Astria then
			local plrTag
			local targetTag
			if plr.Character and targetPlayer.Character then
				plrTag = plr.Character:FindFirstChild('Torso'):FindFirstChild('RANK'):FindFirstChild('RankFrame').Corporation
				targetTag = targetPlayer.Character:FindFirstChild('Torso'):FindFirstChild('RANK'):FindFirstChild('RankFrame').Corporation
			end
			
			if plrTag ~= nil and targetTag ~= nil then
				if plrTag.Visible == true and targetTag.Visible == true then
					if plrTag.TextColor3 ~= targetTag.TextColor3 then
						return false
					else
						return true
					end
				else
					return true
				end
			else
				return true
			end
		else
			return false
		end
	end
end

local function loadAnimation(AnimationObj)
	if not Character then return end
	if loadedAnimations[AnimationObj] then return loadedAnimations[AnimationObj] end
	
	local loadedAnimation = Humanoid:LoadAnimation(AnimationObj)
	loadedAnimations[AnimationObj] = loadedAnimation
	
	return loadedAnimation
end

local function newRay(TargetCFrame)
	if Character and Character:FindFirstChild('Torso') then
		local ray = Ray.new(Character:FindFirstChild('Torso').CFrame.p, (TargetCFrame.p - Character:FindFirstChild('Torso').CFrame.p).unit * 300)
		return ray
	end
end

local function getPartOnRay(ray)
	if ray then
		return workspace:FindPartOnRayWithIgnoreList(ray, ingoreList, false, true)
	end
end

local function visualizeRay(targetPosition, overrideInfo)
	overrideInfo = overrideInfo or {}
	local rayDistance = ((overrideInfo.Origin or equippedWeapon.Barrel.CFrame.p) - targetPosition).magnitude
	
	local Part = Instance.new('Part')
	if overrideInfo and overrideInfo.Color then
		Part.BrickColor = overrideInfo.Color
	elseif LocalPlayer.Team == game.Teams.Astria then
		Part.BrickColor = BrickColor.new('Steel blue') 
	elseif LocalPlayer.Team == game.Teams.Rebels then
		Part.BrickColor = BrickColor.new('Dusty Rose') 
	else
		Part.BrickColor = BrickColor.new('Institutional white') 
	end
	
	Part.Material = overrideInfo.Material or Enum.Material.Neon
	Part.Anchored = true
	Part.CanCollide = false
	Part.Locked = true
	Part.FormFactor = Enum.FormFactor.Custom
	Part.Size = Vector3.new(0.1,0.1,rayDistance)
		
	Part.CFrame = CFrame.new(
		overrideInfo.Origin or equippedWeapon.Barrel.CFrame.p, 
		overrideInfo.Target or targetPosition) * CFrame.new(0,0,-rayDistance/2)
		
	Part.Parent = workspace
	game:GetService('Debris'):AddItem(Part, .05)
end


local function getHumanoid(Object)
	if not Object then return end
	
	if Object.Parent:FindFirstChild('Humanoid') then
		return Object.Parent.Humanoid
	elseif Object.Parent.Parent:FindFirstChild('Humanoid') then
		return Object.Parent.Parent.Humanoid
	end
end

local function updateReticle(mouse)
	if displayHit then
		weaponGUI.Hit.Position = UDim2.new(0, mouse.X - 16, 0, mouse.Y - 16)
	end
	
	if mouse.Target then
		if getHumanoid(mouse.Target) then
			local targetPlayer
			if Players:GetPlayerFromCharacter(Humanoid.Parent) then
				targetPlayer = Players:GetPlayerFromCharacter(Humanoid.Parent)
			end
			if targetPlayer and not tk(LocalPlayer, targetPlayer) then
				mouse.Icon = redIcon
			else
				mouse.Icon = whiteIcon
			end
		else
			mouse.Icon = whiteIcon
		end
	end
end

local function animateCharacter(animationName)
	if equippedWeaponRegister and equippedWeaponRegister.Animations and equippedWeaponRegister.Animations[animationName] then
		equippedWeaponRegister.Animations[animationName]:Play()
	end
end

local function stopSpecificAnimation(animationName)
	if equippedWeaponRegister and equippedWeaponRegister.Animations and equippedWeaponRegister.Animations[animationName] then
		equippedWeaponRegister.Animations[animationName]:Stop()
	end
end

local function stopWeaponAnimations()
	if not equippedWeaponRegister or not equippedWeaponRegister.Animations then return end
	
	for i,v in pairs(equippedWeaponRegister.Animations) do
		v:Stop()
	end
end

local function playSound(soundName)
	if equippedWeaponRegister and equippedWeaponRegister.Sounds and equippedWeaponRegister.Sounds[soundName] then
		equippedWeaponRegister.Sounds[soundName]:Play()
	end
end

--//TOGGLE SPRINT
local function holsterWeapon(newHolsterVal)
	if newHolsterVal then
		if Reloading or isFiring then return end
		isHolstering = true
		
		if Equipped then
			stopSpecificAnimation('Idle')
			animateCharacter('Ease')
		end
		
		if Humanoid and crouchLoop.IsPlaying then
			Humanoid.HipHeight = 0
			crouchLoop:Stop()
		end
		if Humanoid then
			Humanoid.WalkSpeed = 24
		end
	else
		isHolstering = false
		animateCharacter('Idle')
		stopSpecificAnimation('Ease')
		Humanoid.WalkSpeed = 16
	end
end

--//UPDATE UI
local function updateWeaponGUI()
	if equippedWeapon and equippedWeaponRegister and equippedWeaponRegister.Ammo then
		weaponGUI.Hud.Container.Display.Text = equippedWeaponRegister.Ammo..'/'..equippedWeaponInfo.Ammo
		weaponGUI.Hud.Mode.Display.Text = 'Mode: ' .. equippedWeaponRegister.weaponType
		weaponGUI.Hud.Weapon.Display.Text = equippedWeapon.Name
	end
end

--//FIRE WEAPON
local function fireWeapon(mouse)
	canFire = false
	
	if equippedWeapon and Equipped and LocalPlayer.Character and LocalPlayer.Character:FindFirstChild('Head') then
		local targetPosition = mouse.Hit.p
		
		local rayDistance = (LocalPlayer.Character:FindFirstChild('Head') .Position - targetPosition).magnitude
		local aimWithSpread = Vector3.new((targetPosition.x)+(math.random(-(equippedWeaponInfo.Spread/15)*rayDistance,(equippedWeaponInfo.Spread/15)*rayDistance)),(targetPosition.y)+(math.random(-(equippedWeaponInfo.Spread/15)*rayDistance,(equippedWeaponInfo.Spread/15)*rayDistance)),(targetPosition.z)+(math.random(-(equippedWeaponInfo.Spread/15)*rayDistance,(equippedWeaponInfo.Spread/15)*rayDistance)))
		local ray = newRay(CFrame.new(aimWithSpread))
		local hitPart,hitPosition = getPartOnRay(ray)
		local visualRay = visualizeRay(hitPosition)
		
		if hitPart then
			local Humanoid = getHumanoid(hitPart)
			
			if Humanoid then
				local targetPlayer
				if Players:GetPlayerFromCharacter(Humanoid.Parent) then
					targetPlayer = Players:GetPlayerFromCharacter(Humanoid.Parent)
				end
				if targetPlayer and not tk(LocalPlayer, targetPlayer) then
					weaponGUI.Hit.Visible = true
					displayHit = true
					weaponGUI.Hit.Position = UDim2.new(0,mouse.X-16,0,mouse.Y-16)
					if weaponGUI:FindFirstChild('Hit') then
						delay(0.1, 
						function() 
							if weaponGUI and weaponGUI:FindFirstChild('Hit') then
								displayHit = false 
								weaponGUI.Hit.Visible = false 
							end
						end)
					end
				end
			end
			
			local color
			if LocalPlayer.Team == game.Teams.Astria then
				color = BrickColor.new('Steel blue') 
			elseif LocalPlayer.Team == game.Teams.Rebels then
				color = BrickColor.new('Dusty Rose') 
			else
				color = BrickColor.new('Institutional white') 
			end
			
			EVENTS.NETWORKING:FireServer('GunFire', equippedWeapon, hitPart, equippedWeapon.Barrel.Position, targetPosition, color)
		end
	end
	
	spawn(function()
		if equippedWeapon then
			equippedWeapon.Barrel.Flash.Enabled = true
		end
		wait(0.1)
		if equippedWeapon then
			equippedWeapon.Barrel.Flash.Enabled = false
		end
	end)
	
	playSound('Fire')	
	animateCharacter('Recoil')
	equippedWeaponRegister.Ammo = equippedWeaponRegister.Ammo - 1
	updateWeaponGUI()
	wait(equippedWeaponInfo.Rate)
	canFire = true
end

--//RELOAD WEAPON
local function reloadWeapon(Weapon)
	if Reloading then return end
	if isHolstering then
		holsterWeapon(false)
	end
	
	canFire = false
	Reloading = true
	
	animateCharacter('Reload')
	if Weapon.Name ~= 'KRM-1' then
		playSound('MagOut')
	end
	playSound('Reload')
	spawn(function()
		for i = 1 ,3, 1 do
			weaponGUI.Hud.Container.Display.Text = string.rep('.', i)
			if equippedWeaponRegister.Animations then
				wait(equippedWeaponRegister.Animations.Reload.Length/3)
			end
		end
	end)
	
	spawn(function()
		wait(1)
		playSound('MagIn')
		wait(1)
		playSound('Chamber')
	end)
	
	wait(equippedWeaponRegister.Animations.Reload.Length)	
	
	if not equippedWeapon == Weapon then return end
	
	equippedWeaponRegister.Ammo = equippedWeaponInfo.Ammo
	
	updateWeaponGUI()
	
	Reloading = false
	canFire = true
end

--//FIRE LOOP
local function fireLoop(mouse, Weapon)
	if not canFire or not Equipped or not equippedWeapon == Weapon or Reloading or Humanoid.Health <= 0 or not equippedWeaponRegister then return end
	
	if mouseDown then
		if isHolstering then
			holsterWeapon(false)
		end
		
		if equippedWeaponRegister and equippedWeaponRegister.weaponType == 'Auto' then
			while mouseDown and Equipped and equippedWeapon == Weapon and equippedWeaponRegister.Ammo > 0 and canFire and not Reloading and Humanoid.Health > 0 do
				isFiring = true
				fireWeapon(mouse)
				isFiring = false
			end
		elseif equippedWeaponRegister and equippedWeaponRegister.weaponType == 'Semi' and equippedWeaponRegister.Ammo > 0 then
			isFiring = true
			fireWeapon(mouse)
			isFiring = false
		end
		
		if equippedWeaponRegister and equippedWeaponRegister.Ammo and equippedWeaponRegister.Ammo <= 0 then
			playSound('EmptyMag')
			reloadWeapon(Weapon)
		end
	end
end

--//EQUIP WEAPON
local function equipWeapon(mouse, Weapon)
	if not registeredWeapons[Weapon] then warn('[RWEP] Tried to equip unregistered weapon') return end
	if Humanoid.Health <= 0 then return end
	
	equippedWeapon = Weapon
	equippedWeaponInfo = registeredWeapons[Weapon].weaponInfo
	equippedWeaponRegister = registeredWeapons[Weapon]
	Equipped = true
	
	weaponGUI.Hud.Container.Display.Text = equippedWeapon.Name
	weaponGUI.Hud.Mode.Display.Text = registeredWeapons[Weapon].weaponType
	weaponGUI.Hud.Visible = true
	
	updateWeaponGUI()
	
	if not equippedWeaponRegister.Animations['Idle'] then
		local specificAnimations = GUNMOD[Weapon.Name].Anims
		
		for i, v in pairs(specificAnimations) do
			local anim = Instance.new('Animation')
			 anim.AnimationId = 'rbxassetid://' .. v
			
			local loadedAnimation = loadAnimation(anim)
			equippedWeaponRegister.Animations[i] = loadedAnimation
		end
	end
	
	if not equippedWeaponRegister.Sounds['Fire'] then
		for i,v in pairs(Weapon.Handle:GetChildren()) do
			if v:IsA('Sound') then
				equippedWeaponRegister.Sounds[v.Name] = v
			end
		end
	end
	
	animateCharacter('Idle')
	
	mouse.Button1Down:Connect(function()
		mouseDown = true
		fireLoop(mouse, Weapon)
	end)
	
	mouse.Button1Up:Connect(function()
		mouseDown = false
	end)
	
	mouse.Move:Connect(function()
		updateReticle(mouse)
	end)
end

--//UNEQUIP WEAPON
local function unequipWeapon()
	if Humanoid.Health <= 0 then return end
	
	Humanoid.WalkSpeed = 16
	stopWeaponAnimations()
	Equipped = false
	isFiring = false
	Reloading = false
	canFire = true
	
	equippedWeapon = nil
	equippedWeaponInfo = {}
	equippedWeaponRegister = {}
	weaponGUI.Hud.Visible = false
end

--//REGISTER WEAPONS
local function registerWeapon(Weapon)
	if registeredWeapons[Weapon] then return end
	
	local localWeaponInfo = GUNMOD[Weapon.Name]
	
	if localWeaponInfo then
		local color
		if LocalPlayer.Team == game.Teams.Astria then
			color = BrickColor.new('Steel blue') 
		elseif LocalPlayer.Team == game.Teams.Rebels then
			color = BrickColor.new('Dusty Rose') 
		else
			color = BrickColor.new('Institutional white') 
		end
		
		Weapon:WaitForChild('Barrel'):WaitForChild('Flash').Color = ColorSequence.new(color.Color)
		
		local equippedConnection = Weapon.Equipped:Connect(function(mouse)
			equipWeapon(mouse, Weapon)
		end)
		
		local unequippedConnection = Weapon.Unequipped:Connect(unequipWeapon)
		
		registeredWeapons[Weapon] = {
			['equippedConnection'] = equippedConnection;
			['unequippedConnection'] = unequippedConnection;
			['Ammo'] = localWeaponInfo.Ammo;
			['weaponInfo'] = localWeaponInfo;
			['weaponType'] = localWeaponInfo.Mode;
			['Animations'] = {};
			['Sounds'] = {};
		}
	end
end

--//NEW CHARACTER ADDED
local function processNewChar(newCharacter)
	loadedAnimations = {}
	registeredWeapons = {}
	
	local Backpack = LocalPlayer:WaitForChild('Backpack')
	local PlayerGui = LocalPlayer:WaitForChild('PlayerGui')
	
	Character = newCharacter
	Humanoid = newCharacter:WaitForChild('Humanoid')
	
	ingoreList = {}
	table.insert(ingoreList, Character)
	table.insert(ingoreList, workspace:WaitForChild('Ignore'):WaitForChild('VaultDoor'))
	table.insert(ingoreList, workspace:WaitForChild('Ignore'))

   	local animation = Instance.new('Animation')
    animation.AnimationId = 'rbxassetid://1890511540'

	if Character and Humanoid then
   		crouchLoop = Humanoid:LoadAnimation(animation)
	
	
		weaponGUI = PlayerGui:WaitForChild('GunUI')
		
		Backpack.ChildAdded:Connect(function(Child)
			if Child:IsA('Tool') then
				registerWeapon(Child)
			end
		end)
		
		Backpack.ChildRemoved:Connect(function(Child)
			if Child:IsA('Tool') then
				if Child.Parent == Character then return end
				
				if registeredWeapons[Child] then
					registeredWeapons[Child]['equippedConnection']:Disconnect()
					registeredWeapons[Child]['unequippedConnection']:Disconnect()
					registeredWeapons[Child] = nil
				end
			end
		end)
		
		Humanoid.Died:Connect(function()
			Humanoid:UnequipTools()
			
			for i,v in pairs(Backpack:GetChildren()) do
				if registeredWeapons[v] then
					registeredWeapons[v]['equippedConnection']:Disconnect()
					registeredWeapons[v]['unequippedConnection']:Disconnect()
				end
			end
			
			registeredWeapons = {}
			equippedWeapon = nil
			equippedWeaponInfo = {}
			equippedWeaponRegister = {}
			Equipped = false
		end)
		
		Humanoid.Jumping:Connect(function(state)
			if state and crouchLoop.IsPlaying then
				Humanoid.HipHeight = 0
				Humanoid.WalkSpeed = 16
				crouchLoop:Stop()
			end
		end)
		
		for i,v in pairs(Backpack:GetChildren()) do
			if v:IsA('Tool') then
				if not registeredWeapons[v] then
					registerWeapon(v)
				end
			end
		end
	end
	
	sprintBtn = weaponGUI:WaitForChild('Hud'):WaitForChild('Mobile'):WaitForChild('Sprint'):WaitForChild('Button')
	crouchBtn = weaponGUI:WaitForChild('Hud'):WaitForChild('Mobile'):WaitForChild('Crouch'):WaitForChild('Button')
	reloadBtn = weaponGUI:WaitForChild('Hud'):WaitForChild('Mobile'):WaitForChild('Reload'):WaitForChild('Button')
	
	if sprintBtn then
		sprintBtn.MouseButton1Click:Connect(function()
			if not isHolstering then
				holsterWeapon(true)
			else
				holsterWeapon(false)
			end
		end)
	end

	if crouchBtn then
		crouchBtn.MouseButton1Click:Connect(function()
			if Equipped == true and Humanoid then
				if not (crouchLoop.IsPlaying) then
					holsterWeapon(false)
					Humanoid.HipHeight = -.8
					Humanoid.WalkSpeed = 9
					crouchLoop:Play()
				else
					Humanoid.HipHeight = 0
					Humanoid.WalkSpeed = 16
					crouchLoop:Stop()
				end
			end
		end)
	end
	
	if reloadBtn then
		reloadBtn.MouseButton1Click:Connect(function()
			if Equipped and equippedWeaponRegister and equippedWeaponInfo and equippedWeaponRegister.Ammo < equippedWeaponInfo.Ammo then
				reloadWeapon(equippedWeapon)
			end
		end)
	end
end
LocalPlayer.CharacterAdded:Connect(processNewChar)

--//RENDER REQUEST
local function handleRender(Target, Origin, Color, Raycount, Spread)
	visualizeRay(Target, {Origin = Origin, Color = Color})
end

RENDER.OnClientEvent:Connect(handleRender)

--//USER INPUT
UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent  then return end
	if input.UserInputType == Enum.UserInputType.Keyboard then
		if input.KeyCode == Enum.KeyCode.R and not Reloading then
			if Equipped and equippedWeaponRegister.Ammo < equippedWeaponInfo.Ammo then
				reloadWeapon(equippedWeapon)
			end
		elseif input.KeyCode == Enum.KeyCode.F  then
			if not isHolstering then
				holsterWeapon(true)
			else
				holsterWeapon(false)
			end
		elseif input.KeyCode == Enum.KeyCode.C then
			if Equipped == true and Humanoid then
				if not (crouchLoop.IsPlaying) then
					holsterWeapon(false)
					Humanoid.HipHeight = -.8
					Humanoid.WalkSpeed = 9
					crouchLoop:Play()
				else
					Humanoid.HipHeight = 0
					Humanoid.WalkSpeed = 16
					crouchLoop:Stop()
				end
			end
		end
	elseif (input.UserInputType == Enum.UserInputType.Gamepad1 and input.KeyCode == Enum.KeyCode.ButtonL2) then
		if not isHolstering then
			holsterWeapon(true)
		else
			holsterWeapon(false)
		end
	elseif (input.UserInputType == Enum.UserInputType.Gamepad1 and input.KeyCode == Enum.KeyCode.DPadRight) then
		if Equipped and equippedWeaponRegister.Ammo < equippedWeaponInfo.Ammo then
			reloadWeapon(equippedWeapon)
		end
	elseif (input.UserInputType == Enum.UserInputType.Gamepad1 and input.KeyCode == Enum.KeyCode.DPadLeft) then
		if Equipped == true and Humanoid then
			if not (crouchLoop.IsPlaying) then
				holsterWeapon(false)
				Humanoid.HipHeight = -.8
				Humanoid.WalkSpeed = 9
				crouchLoop:Play()
			else
				Humanoid.HipHeight = 0
				Humanoid.WalkSpeed = 16
				crouchLoop:Stop()
			end
		end
	end
end)
1 Like

Try setting the network ownership of the hit part to the server:

a good example of what your trying to accomplish is also here:

1 Like

I’ll try this out and get back to you, thanks.

1 Like