Replicating paintball bullets across client-server-client

  • Note I am using old code (from Daxter33’s paintball game) and thus FilteringEnabled wasn’t a thing back then

I currently do this via the client, which works great, for creating the bullet on the client. However, I want bullets visible on all clients (so you can see bullets being fired at you)

However, I don’t think doing all this on the server is smart, as it may cause lag. So what should I be passing to the server, then back to the client?

--// Create the projectile
local function Projectile(weapon, start, target, damage, speed, drop, color)
	local Collide = nil
	local CollidePosition = nil
	local Ratio = 10
	local Direction = (target - start).Unit
	local Hit, EndPosition = CastRay(start, target)
	local Distance = (EndPosition - start).Magnitude
	local BulletDrop = (drop / Ratio) / 250
	local Length = speed / Ratio
	local PrevPosition = start
	
	-- Create the bullet object
	local Bullet = Instance.new("Part")
	Bullet.BrickColor = color
	Bullet.Material = Enum.Material.SmoothPlastic
	Bullet.Anchored = true
	Bullet.CanCollide = false
	Bullet.CFrame = CFrame.new(start, EndPosition)
	
	Bullet.Parent = BulletsContainer
	
	Debris:AddItem(Bullet, 5) -- Bullet should only last 5 seconds
	
	if Distance > Length then
		while true do
			local BulletFront = (Bullet.CFrame * CFrame.new(0, 0, -1)).Position - Vector3.new(0, BulletDrop, 0)
			local Direction = (BulletFront - Bullet.CFrame.Position).Unit
			local Position = PrevPosition + (Direction * Length)
			local Hit, EndPosition = CastRay(Bullet.CFrame.Position, BulletFront)
			local NewDistance = (EndPosition - PrevPosition).Magnitude
			
			CollidePosition = EndPosition
			Collide = Hit
			
			if NewDistance < Length then break end
			
			local BulletDistance = (Position - PrevPosition).Magnitude
			Bullet.Size = Vector3.new(0.5, 0.5, 5)
			Bullet.CFrame = CFrame.new(PrevPosition, Position) * CFrame.new(0, 0, -BulletDistance / 2)
			PrevPosition = Position
			
			wait()
		end
		
		Bullet:Destroy() -- Destroy the bullet
	else
		print("WHY PRINT?")
		local Position = PrevPosition + (Direction * Length)
		local Hit, EndPosition = CastRay(start, Position)
		local Distance = (EndPosition - start).Magnitude
		
		Bullet.Size = Vector3.new(0.1, 0.1, Distance)
		Bullet.CFrame = CFrame.new(start, EndPosition) * CFrame.new(0, 0, -Distance / 2)
		
		Debris:AddItem(Bullet, 0.1) -- Remove bullet basically immediately
		
		CollidePosition = EndPosition
		Collide = Hit
	end
	
	CheckCollision(Collide, CollidePosition, color, damage, weapon) -- Bullet has hit something
	
	return CollidePosition
end

--// Fire the bullet
function Bullet.Fire(weapon, start, target, damage, speed, drop, color)
	coroutine.wrap(function()
		Projectile(weapon, start, target, damage, speed, drop, color)
	end)()
end

PLEASE DON’T RECOMMEND FASTCAST!!! I don’t want to use it/tried, doesn’t give off what I am going for + I like using code I can understand

You can set the network of the paintball to nil meaning the server will it’s be network but since you said the server is more delayed and laggier but in the client it’s smoother, you could make the local script fire a remote then deliver a variable of the part, etc. the regular script would then receive a call from the remote that it has been fired, it would then set the network of the delivered part variable to nil “part:SetNetworkOwner(nil)” since only on the regular script can do that then fire all the clients the delivered part then do everything you must do.

But since you fired all the clients, every parts will not look the same on each player’s screen so you have to create a invisible part hitbox in the server and make it delete when the ancestry of the part has changed, sorry if you don’t understand me but you could look for more answers.

You should look into remote events

Yes they are also quite easy to understand here is the page:

1 Like

You can use math and rays to do the projectile’s movement and timing on the server, or even on the client, and verifying it generally on the server.

You could even handle hits on the client, and only verify it on the server once the client sends the information that they shot something, and punish them for being wrong (or make it more strict.) You could log old positions of players too, to have a general frame of which they can be hit in, to be forgiving on them. (think going back in time and checking within X amount of time for a buffer frame of people’s old positions for where they were due to ping)

Very recommended to use remote events and stuffs. You can’t damage players with client scripts.

Tried making the most minimalistic paintball gun possible in 5 minutes. Works pretty well but there’s still a ping delay which could be optimized with good server/client communication (I suck at making ranged weapons)
As @Lua_Basics mentioned, you should verify everything on the server, you can run the visuals on the client and assume they hit if they did, then verify on the server using a remote function and just not damage the person if it looks like they couldn’t possibly have hit them.

local bullet1 = Instance.new("Part")
bullet1.CanCollide = false
bullet1.Size = Vector3.new(0.5,0.5,0.5)

local attachment = Instance.new("Attachment",bullet1)
attachment.WorldPosition = bullet1.Position

local force = Instance.new("VectorForce",bullet1)
force.Attachment0 = attachment
force.Force = Vector3.new(0,20,-20)

script.Parent.Activated:connect(function()
	local bullet = bullet1:Clone()
	game:GetService("Debris"):AddItem(bullet.VectorForce,0.3)
	bullet.CFrame = script.Parent.Handle.CFrame
	connection = bullet.Touched:connect(function(hit)
		local hum = hit.Parent:FindFirstChild("Humanoid")
		if hit and hit.Parent ~= script.Parent and hit.Parent ~= script.Parent.Parent and hum then
			hum:TakeDamage(10)
			bullet:Destroy()
			connection:Disconnect()
		end
	end)
	bullet.Parent = workspace
	bullet:SetNetworkOwner(nil)
end)

This is a server script inside of a tool with just a handle

1 Like

Generally for client/server communication, I like to assume everything is untampered on the client then make sure it’s really untampered on the server.