Same raycast gives a slightly different answer depending on if it's done on the client or server

I am currently working on custom netcode. The client generates a command, predicts the outcome of it, and then sends that command to the server. The server then executes the command and sends back the outcome to client, so that the client can verify if its prediction was accurate. Ideally, the player should not make be making mispredictions with my current movement system, unless that player was cheating. A movement on the client should perfectly mirror what eventually happens on the server.

However, the client is actually very slightly desyncing. My movement system uses raycasts, and curiously, a raycast on the client and a raycast on the server given the same inputs provide ever so slightly different answers. Perhaps either the client or server calculates things slightly more precisely?

I’ve got a bandaid solution, where when comparing two Vector3s, instead of checking if they’re perfectly equal, I can have some tolerances and say “close enough.” However, this is an inelegant solution. Would anyone happen to know why this happens, and if there’s a way to avoid this? Thanks.

I’ve isolated this to its own small rbxl so that you can test it for yourself.
raycast_differences.rbxl (54.5 KB)

1 Like

It seems like replication compression, print out the part’s cframe and they are different on the client and the server.


If I copy the CFrame values from the server and set them on the client i get the same result for both.

local module = {}

wait(1)

local params = RaycastParams.new()
params.FilterDescendantsInstances = {}
params.FilterType = Enum.RaycastFilterType.Exclude
params.IgnoreWater = true
params.CollisionGroup = "Default"
params.RespectCanCollide = true
params.BruteForceAllSlow = false

workspace.Part.CFrame = CFrame.new(-1, 4.00000143, -0.133974373, 1, 0, 0, 0, 0.866025627, -0.500000119, 0, 0.500000119, 0.866025627)

print(workspace:Raycast(Vector3.new(-0.423, 11.231, 0.142),Vector3.new(-0.423, -9.83, 0.142), params))
print(workspace.Part.CFrame)

return module

So, the inconsistent raycast result is from the cframe not being exact due to (slightly) lossy replication. I hope this helps!

1 Like

Ah, thank you! I never considered that it could have something to do with the parts, but now I’ve got an idea of how to fix things.

Update: I found that Vector3s are not compressed by RemoteEvents, so I sent those over instead of the CFrame object itself, and then reconstructed it with CFrame.fromMatrix(). Small note, I had to flip the LookVector, otherwise the reconstructed CFrame would make the part look inside out.

LookVector = -ZVector
The ‘front’ of a CFrame is actually toward its negative Z and the positive Z is toward its back.
If you look at the XZ plane from above, then X will be to the right and Z will be to the bottom, much like how pixels are laid out on the screen (and unlike how geometry/coordinate systems are usually taught in school, where X is right and the other axis is top, not bottom). But if you move a part forward and to the right, it will go up and right.
Use [XYZ]Vector instead of [XY]Vector and LookVector.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.