Raycasting on server doesn't replicate correctly to client

Hello. I’m making an ice bullet of sorts that is created and shot by a module script run by a server script. The script works fine but the bullet ends up in the wrong position for the client who fired it and correctly on the server.

Ex:
this is what the server-side sees (this is what it should look like)
mindMelting

This is what the client sees
mindmelting2


When the icicles are shot, a ray is casted consistently, and when the ray hits something that’s close enough to the icicle, the icicles position goes to the rays hit position.

here’s the code:

Code
--[[ Services ]]--
local ServerStorage = game:GetService("ServerStorage")
local RunService	= game:GetService("RunService")

--[[ Variables ]]--
local projectilesFolder = workspace.Projectiles
local particlesFolder = workspace.Particles
local iceFolder		= ServerStorage.Spells.Ice
local icicle 		= iceFolder:WaitForChild("Icicle")
local icicleExplosion = iceFolder:WaitForChild("IceExplosin")

--[[ Custom Variables ]]--
local SPEED_OF_ICEICLE = 100
local LIFETIME = 5
local DAMAGE = 10

--[[ Functions ]]--
local function shootIce(hmnRP)
	
	local ORIGIN_POINT = (hmnRP.CFrame * CFrame.new(0, 0, -3)).Position
	local TARGET = (hmnRP.CFrame * CFrame.new(0, 0, 7)).Position
	
	local velocity = (ORIGIN_POINT - TARGET).unit * SPEED_OF_ICEICLE
	
	local clonedIcicle = icicle:Clone()
	clonedIcicle:SetPrimaryPartCFrame(CFrame.new(ORIGIN_POINT) * (hmnRP.CFrame - hmnRP.Position))
	local originalRotation = hmnRP.CFrame - hmnRP.Position
	clonedIcicle.Parent = projectilesFolder
	
	local bulletPos, bulletVelocity = ORIGIN_POINT, velocity
	
	local startTime = DateTime.now().UnixTimestamp
	while DateTime.now().UnixTimestamp - startTime < LIFETIME do
		
		local dt = RunService.Heartbeat:Wait()

		local bulletRay = Ray.new(bulletPos, bulletVelocity)
		local hit, position = workspace:FindPartOnRayWithIgnoreList(bulletRay, projectilesFolder:GetDescendants())
		
		if hit and (position - bulletPos).magnitude < 2 then --Doesn't position in the wall
			print(position)
			clonedIcicle:SetPrimaryPartCFrame(CFrame.new(position) * originalRotation)
			local hmn = hit.Parent:FindFirstChild("Humanoid")
			if hmn then
				if not hit:IsDescendantOf(hmnRP.Parent) then --Doesn't always hit hmn
					local clonedExplosin = icicleExplosion:Clone()
					clonedExplosin.Parent = particlesFolder
					clonedExplosin.Position = position
					hmn:TakeDamage(DAMAGE)
					clonedIcicle:Destroy()
					wait(.2)
					clonedExplosin.IceExplosin.Rate = 0
					wait(4)
					clonedExplosin:Destroy()
					return
				end
			elseif hit:IsA("BasePart") and not hmn then
				clonedIcicle.PrimaryPart.Anchored = true
				clonedIcicle.PrimaryPart.CanCollide = false
				clonedIcicle.PrimaryPart.ForceFieldGlow:Destroy()
				wait(.5)
				clonedIcicle.PrimaryPart.ParticleEmitter.Rate = 0
				clonedIcicle.PrimaryPart.Transparency = .7
				wait(30)
				break
			end
		end
		bulletPos = bulletPos + bulletVelocity * dt
		if not clonedIcicle then break end
		clonedIcicle:SetPrimaryPartCFrame(CFrame.new(bulletPos) * originalRotation)
	end
	clonedIcicle:Destroy()
end

return shootIce

The code is in a module script ran by a server script that receives a remote event from the client.
The icicles physics are calculated by the script, not by server physics.

I’m not totally sure how it functions, but I’m assuming that you send a remote event/function event between the client and the server.

Try having the server create a part representing the server’s raycast (using the position the ray hit’s in order to get the midpoint with the starting position and then angling the part with CFrame in order for it to point from the start to the endpoint), and then do the same on the client with a different color. You may see some or no difference.

It’ll be helpful for testing things.

Another thing to note is that not everything replicates between the server and the client, though I believe you already knew that. It may be that the character is moving and therefore not exactly in the same place as the server (due to ping). Doesn’t entirely explain the parallax even while studio testing, but you can always simulate a heavy load internet by increasing replicating time somewhere in the studio settings.

1 Like

I do send a remote event that tells the server to create the bullet and shoot it on the server. I didn’t, or don’t know how to do that second part, but I did print out the position from the server and checked if the client’s side matched up, and it didn’t.

I actually didn’t know this. I tried doing the heavy load internet thing but I didn’t see much difference with where the icicle hit the wall.

Hm, everything is the same between both? You might just need to leave it on the server, and just let the workspace replicate rather than have the server and the client do it both.

1 Like

What do you mean when you say leave it on the server? How would I get the workspace to replicate?

The server naturally replicates to the client. Due to FilteringEnabled anything done on the client doesn’t replicate to the server. That’s what I meant by having two different visualizing scripts for the raycasts.

So like, fire the module script on the client and the server except the server wouldn’t replicate it back to the client? How do I play it on the server without making double for the person who fired it. I’m really confused.

I figure out what the problem was but I can’t make sense of it. I noticed that when the bullet landed it looked like it bounced off the wall for a split sec for the client and only the client. I assume the part was being placed in the right place but because the part wasn’t anchored till after it checked if the ray hit a humanoid, it had a split sec to act on the servers physics, bounced glitched out the wall, then was anchored. All I had to do was move the line where is set the parts position after the part was anchored. Honestly surprised I found this. But anyways thank you for the help!

1 Like