Optimizing AI Remote event & Bindable event data

Hello developers, I am having a problem with AI bots sending data via remote events & bindable events, when there’s 25+ bots firing at the same time, ping dramatically increases overtime.

Before I compressed Vector3 data in an array, it was really laggy when 5 bots fired at the same time, but I compressed the data with the decodeArray & encodeArray function that you’ll see in the code below. I feel like I did the compression wrong and want to compress the array data further.

A solution I’m looking for is compressing data an array has, I heard spamming remote events doesn’t matter but what the data I’m sending does.

PS: I don’t use humanoids.
AI Handler Script:

    heavy.FiredSignal:Connect(function(bulletInfo) -- FiredSignal does get disconnected when they die.
		if not bulletInfo then return end
		for i, plr in game.Players:GetPlayers() do
			local visualRemotes = plr:FindFirstChild("VisualRemotes")
            -- replicate bullets to clients to render them
			if visualRemotes then visualRemotes.MinigunFire:FireClient(plr, bulletInfo, false) end
		end
	end)

AI Module (IN OOP):


local AI.BulletInfo = {
	Damage = 9, --(Chance and 9) or 35,
	Speed = 6,
	tick = 0, 
	isDistanceBased = true,
	maxTick = 15,
}

local function encodeArray(array)
	for i, property in array do
		if typeof(property) == "Vector3" then
			local vectorArray = {
				X = property.X,
				Y = property.Y,
				Z = property.Z
			}
			local encodedVectorString = Services.HttpService:JSONEncode(vectorArray)
			array[i] = nil
			array[i] = encodedVectorString
		else
			array[i] = property
		end
	end
	return array
end

function AI:fireBullet(TargetPosition, TargetVelocity)
	if not TargetPosition or not self then return end
	local between = (TargetPosition - self.Torso.Position)
	local spread = heavyAI.pineConeSpread(self.Torso.CFrame.LookVector, math.rad(5)) * heavyAI.BulletInfo.Speed
	local clonedBulletinfo = table.clone(AI.BulletInfo)
	clonedBulletinfo.shooterRoot = self.Root
	clonedBulletinfo.isCrits = self.PermaCrits or (math.random(1, 10) == 1)
	clonedBulletinfo.Damage = (clonedBulletinfo.isCrits and clonedBulletinfo.Damage * 3) or clonedBulletinfo.Damage
	clonedBulletinfo.position = self.Torso.Position
	clonedBulletinfo.direction = spread
	clonedBulletinfo = encodeArray(clonedBulletinfo)
-- the 2 last code lines below is where it sends the data to clients and the parallel bullet handler.
	script.Parent.Parent.Parent.aiProjectiles.sendInfo:Fire(clonedBulletinfo) 
	self.FiredSignal:Fire(clonedBulletinfo)
end

Bullet Handler
(bulletHandler module is ran using OOP)

local HttpService = game:GetService("HttpService")
local bulletHandler = require(script.aiBulletHandler)
local bullets = {}

local function decodeArray(array)
	local newArray = {}
	for i, o in array do
		if typeof(o) == "string" then
			local vectorArray = HttpService:JSONDecode(o)
			if vectorArray then
				newArray[i] = Vector3.new(vectorArray.X, vectorArray.Y, vectorArray.Z)
			else
				newArray[i] = o
			end
		else
			newArray[i] = o
		end
	end
	array = nil
	return newArray
end

task.synchronize()
script.Parent.sendInfo.Event:Connect(function(bulletInfo)
	table.insert(bullets, bulletHandler.new(decodeArray(bulletInfo)))
end)

task.desynchronize()
task.spawn(function()
	while true do
		for i, bullet in (bullets) do
			if bullet then
				if bullet.isAlive then
					bullet:Travel()

					if bullet.isAlive and bullet.tick >= bullet.maxTick then
						bullet:Destroy()
						table.remove(bullets, table.find(bullets, bullet))
						bullets[i] = nil
					end
				else
					table.remove(bullets, table.find(bullets, bullet))
					bullets[i] = nil
				end
			end
		end
		task.wait()
	end
end)

Please let me know if I’ve done anything wrong here, thank you!