How can I improve my FastCast system?

Hello, I’m trying to improve my FastCast gun system for my mecha game but my concern is that ping and client-server communication is somewhat unsatisfactory. I want to be able have very performant client-server communication for the code that would allow up to at least 12 simultaneous players each holding at least 2-4 weapons at the same time.

I have considered offloading networking to player rather than the server but I’m not sure how effective that would be and I’m not sure how to do it myself.

One important thing to note is that performance in studio is very satisfactory however performance on live server is unsatisfactory as ping skyrockets.

I want be able to have an absolute metric ton of “projectiles” at anyone time as a sort of factor of safety for what the game should be able to handle at minimum ping.

This code handles the client

local UIS = game:GetService("UserInputService")
local RS = game:GetService("ReplicatedStorage")
local RunS = game:GetService("RunService")

local Gun = script.Parent
local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()

--local Ammo30mm = script.Parent:GetAttribute("PriAmmo")
--local Ammo120mm = script.Parent:GetAttribute("AltAmmo")

local heldFire = false
local lastFire = 0

local fireEvent = Gun:WaitForChild("GunFire")
local Attributes = Gun:GetAttributes()


local HUD = player.PlayerGui.TSFHUD

local function fire()
	local now = tick()
	--print("Fired function")
	local mousePos = mouse.Hit.Position
	if Gun:GetAttribute("Mode") == "Pri" and ((Gun:GetAttribute("Hardpoint") == "LH") or (Gun:GetAttribute("Hardpoint") == "RH")) then
		--print(tick() - now)
		if now - lastFire >= .01 then
			--print("Fired Pri")
			lastFire = now
			if Attributes["PriAmmo"] > 0 then
				fireEvent:FireServer(mousePos)
				Attributes["PriAmmo"] -= 1
				if Attributes["Hardpoint"] == "LH" then
					HUD.ARML.AMMO.Text = Attributes["PriAmmo"]
				elseif Attributes["Hardpoint"] == "RH" then
					HUD.ARMR.AMMO.Text = Attributes["PriAmmo"]
				end
			end
		end
	elseif Gun:GetAttribute("Mode") == "Alt" and ((Gun:GetAttribute("Hardpoint") == "LH") or (Gun:GetAttribute("Hardpoint") == "RH")) then
		if now - lastFire >= 1 then
			--print("Fired Alt")
			lastFire = now
			if Attributes["AltAmmo"] > 0 then
				fireEvent:FireServer(mousePos)
				Attributes["AltAmmo"] -= 1
				if Attributes["Hardpoint"] == "LH" then
					HUD.ARML.AMMO.Text = Attributes["AltAmmo"]
				elseif Attributes["Hardpoint"] == "RH" then
					HUD.ARMR.AMMO.Text = Attributes["AltAmmo"]
				end
			end
			
		end
	end
end

RunS.RenderStepped:Connect(function()
	if heldFire then
		fire()
	end
end)

UIS.InputBegan:Connect(function(i,gp)
	if not gp and i.UserInputType == Enum.UserInputType.MouseButton1 then
		heldFire = true
	end
end)

UIS.InputEnded:Connect(function(i,gp)
	if not gp and i.UserInputType == Enum.UserInputType.MouseButton1 then
		heldFire = false
	end
end)

Gun.Equipped:Connect(function()

	if Attributes["Hardpoint"] == "LH" then
		HUD.ARML.NAME.Text = Attributes["DisplayName"]
		if Attributes["Mode"] == "Pri" then
			HUD.ARML.AMMO.Text = Attributes["PriAmmo"]
			HUD.ARML.MODE.FIREMODE.Text = Attributes["PriAmmoType"]
		elseif Attributes["Mode"] == "Alt" then
			HUD.ARML.AMMO.Text = Attributes["AltAmmo"]
			HUD.ARML.MODE.FIREMODE.Text = Attributes["AltAmmoType"]
		end
	elseif Attributes["Hardpoint"] == "RH" then
		HUD.ARMR.NAME.Text = Attributes["DisplayName"]
		if Attributes["Mode"] == "Pri" then
			HUD.ARMR.AMMO.Text = Attributes["PriAmmo"]
			HUD.ARMR.MODE.FIREMODE.Text = Attributes["PriAmmoType"]
		elseif Attributes["Mode"] == "Alt" then
			HUD.ARMR.AMMO.Text = Attributes["AltAmmo"]
			HUD.ARMR.MODE.FIREMODE.Text = Attributes["AltAmmoType"]
		end
	elseif Attributes["Hardpoint"] == "LP" then
		HUD.PYLONL.NAME.Text = Attributes["DisplayName"]
		if Attributes["Mode"] == "Pri" then
			HUD.PYLONL.AMMO.Text = Attributes["PriAmmo"]
			HUD.PYLONL.MODE.FIREMODE.Text = Attributes["PriAmmoType"]
		elseif Attributes["Mode"] == "Alt" then
			HUD.PYLONL.AMMO.Text = Attributes["AltAmmo"]
			HUD.PYLONL.MODE.FIREMODE.Text = Attributes["AltAmmoType"]
		end
	elseif Attributes["Hardpoint"] == "RP" then
		HUD.PYLONR.NAME.Text = Attributes["DisplayName"]
		if Attributes["Mode"] == "Pri" then
			HUD.PYLONR.AMMO.Text = Attributes["PriAmmo"]
			HUD.PYLONR.MODE.FIREMODE.Text = Attributes["PriAmmoType"]
		elseif Attributes["Mode"] == "Alt" then
			HUD.PYLONR.AMMO.Text = Attributes["AltAmmo"]
			HUD.PYLONR.MODE.FIREMODE.Text = Attributes["AltAmmoType"]
		end
	end
end)

This code handles the server

local RS = game:GetService("ReplicatedStorage")
local FireEvent = script.Parent:WaitForChild("GunFire")
local Debris = game:GetService("Debris")

local Gun = script.Parent


local FirePoint30 = Gun.GWS9.FirePoint30
local FirePoint120 = Gun.GWS9.FirePoint120

local Attributes = Gun:GetAttributes()

local RNG = Random.new()
local TAU = math.pi*2

local FastCast = require(Gun.FastCastRedux)
local PartCache = require(Gun.PartCache)

local bulletsFolder = workspace.Bullets
local bullet30 = bulletsFolder.Bullet30
local bullet120 = bulletsFolder.Bullet120

local Caster = FastCast.new()
local MaxRange = 2000

local CastParams = RaycastParams.new()
CastParams.FilterType = Enum.RaycastFilterType.Blacklist
CastParams.IgnoreWater = true

local BulletCache = PartCache.new(bullet30, 50, bulletsFolder)

local CastBehaviour = FastCast.newBehavior()
CastBehaviour.RaycastParams = CastParams
CastBehaviour.Acceleration = Vector3.new(0,-workspace.Gravity,0)
CastBehaviour.AutoIgnoreContainer = true
CastBehaviour.HighFidelityBehavior = FastCast.HighFidelityBehavior.Default
CastBehaviour.MaxDistance = MaxRange
CastBehaviour.CosmeticBulletContainer = bulletsFolder
CastBehaviour.CosmeticBulletProvider = BulletCache

local impactParticles = bulletsFolder.EffectPart.Effect:GetChildren()

local function makeFX(position,normal)
	local attachment = Instance.new("Attachment")
	attachment.CFrame = CFrame.new(position,position+normal)
	attachment.Parent = workspace.Terrain
	for i,v in pairs(impactParticles) do
		local particle = v:Clone()
		particle.Parent = attachment
		Debris:AddItem(attachment,particle.Lifetime.Max	)
	end
	
end

local function onRayHit(cast,result,velocity,bullet)
	local hit = result.Instance
	local hitPoint = result.Position
	local normal = result.Normal
	
	local character = hit:FindFirstAncestorWhichIsA("Model")
	if character and character:FindFirstChild("Humanoid") then
		character.Humanoid:TakeDamage(10)
	end
	--makeFX(hitPoint,normal)
end

local function onRayTerminated(cast)
	local cosmeticBullet = cast.RayInfo.CosmeticBulletObject
	if cosmeticBullet ~= nil then
		if CastBehaviour.CosmeticBulletProvider ~= nil then
			CastBehaviour.CosmeticBulletProvider:ReturnPart(cosmeticBullet)
		else
			cosmeticBullet:Destroy()
		end
	end
end

local function onLengthChanged(cast,lastPoint,direction,length,velocity,bullet)
	if bullet then
		local bulletLength = bullet.Size.Z/2
		local offset = CFrame.new(0,0, -(length - bulletLength))
		bullet.CFrame = CFrame.lookAt(lastPoint,lastPoint+direction):ToWorldSpace(offset)
	end
end

local function firePrimary(mousePos)
	local origin = FirePoint30.WorldPosition
	local direction = (mousePos - origin).Unit
	local directionalCF = CFrame.new(Vector3.new(),direction)
	local direction = (directionalCF * CFrame.fromOrientation(0,0,RNG:NextNumber(0,TAU))* CFrame.fromOrientation(math.rad(RNG:NextNumber(Attributes["PriMinSpread"],Attributes["PriMaxSpread"])),0,0)).LookVector
	local modifiedBulletSpeed = direction*1000
	Caster:Fire(origin,direction,modifiedBulletSpeed,CastBehaviour)
end

local function fireAlt()
	
end

FireEvent.OnServerEvent:Connect(function(Player,MousePos)
	if Attributes["Mode"] == "Pri" then
		firePrimary(MousePos)
	elseif Attributes["Mode"] == "Alt" then
		fireAlt()
	end
end)
Caster.LengthChanged:Connect(onLengthChanged)
Caster.RayHit:Connect(onRayHit)
Caster.CastTerminating:Connect(onRayTerminated)


Gun.Equipped:Connect(function()
	CastParams.FilterDescendantsInstances = {Gun.Parent, bulletsFolder}

end)

The code is mainly taken away from the main fastcast debug gun but something just doesnt seem right with the high ping.

I have a file ready to give away if anyone needs it but right now I want to see what can be done before having to resort to that.

Thanks in advance.

The major issue in your case are multiple server scripts.
In order to significantly improve the performance you need to make one handler for all the guns in ServerScriptService.

I see, but how would I go about making casters for each gun then?

If I understand correctly, I should have a server script that handles whenever a client fires a gun, find which gun fired and where? I’m not really sure how I would go about making casters for the gun in this method.

Or would I only need one caster for everything?

Create casters for all the available guns and when someone fires, pick the one you want.

Use a remote event to detect whenever player attempts to shot.

Remote events already detect when clients shoot to the server with that being the main issue in the first place. How is using another remote event gonna make it better?

Sorry man, I aint following, youre gonna have to give me a simple example of what you mean.