Help with laser not being returned from server in RemoteFunction

I’m trying to make a laser attach to my gun, so I’m calling the function off of the client which will trigger the server script to return the laser beam. However, I am trying to set the length of the beam to the size of ray that is casted out, so the beam works like an actual laser. The issue is I get this error: attempt to index nil with ‘Size’.

Error is called on this line: laser.Size = Vector3.new(0.3, 0.3, dist)

This is the local script

laser = laserRemote:InvokeServer()
spawn(function()
    RunService.Stepped:Connect(function()
	    if laserOn == false then return end
		print('Fire a laser')
		local direction = Handle.CFrame.LookVector.Unit
		local laserRay = Ray.new(Handle.CFrame.p, direction * 2000)
		local _,hitPos = workspace:FindPartOnRay(laserRay, Handle, true, true)
		local dist = (Handle.CFrame.p - hitPos).magnitude
		print(hitPos)
		print(dist)
		laser.Size = Vector3.new(0.3, 0.3, dist)
		laser.CFrame = CFrame.new(Handle.CFrame.p, hitPos) * CFrame.new(0, 0, -dist / 2)
		laser.Parent = Handle
		wait()
		laser:Destroy()
	end)
	print('End')
	return
end)

and this is the server remote function

function LaserRemote.OnServerInvoke()

local laser = IdealLaser:Clone()

return laser

end

The IdealLaser is defined on server, so it’s not an issue of cloning that.
I will also need help setting the network ownership of the laser beam to the client so it doesn’t lag for the client but all other players will still be able to see it, but I can do that later. So if you can figure out the error, please help.

Where is the cloned laser in your place? Make sure it’s not somewhere the client can’t access such as ServerStorage.

You only create one laser in your script and destroy it at the end of the function. As you’re attempting to do this every step it’s going to error as you destroyed it the first time it ran.

Why are you creating the laser on the server anyway? Any changes you make to it won’t replicate back so only the client who fired will be able to see it

It doesn’t exist, I create it on the server script and clone it to the remote function.

I’m going to set network ownership to client, so everyone can see it. I also will fix it destroying, but that’s not the error right now that I need help with.

That’s not how network ownership works. It won’t make the part replicate it’ll just make the player calculate the physics for that part

I’ll try to figure it out, but I guess it kinda makes more sense to add the lasers just on the client.

Also there is no need to spawn() the .Stepped event, events don’t yield and the script will keep on working.

Can you explain this a bit more in depth in terms of terminology? What does it mean when something yields? Why not use the spawn() function? Thank you.

Only the client can see the cloned part, so the server reads it as “nil”. You will have to send the information of the laser from the client to the server, and the server then uses the information to create the laser.

But to update the laser each time with it’s new length captured by the ray would be exhausting for the server and create some much unwanted latency.

The size will never replicate from client to server, regardless of the parts network owner, so, the server has to create the part and manipulate the part, though you can change the part on the client, send the info, and the server will change the part with the info.

Still, to be sending the laser info endlessly would create a bunch of lag, no?

This might help.

When something yields, it means that the script will stop at the line that yielded until it’s done with what it’s supposed to do.

print("hi")
wait(3) --yields for 3 seconds
print("bye") --this isn't executed until the previous line is done

Other stuff that yield would be :WaitForChild(), which will yield until the child to wait for has loaded. You use spawn() and other things like coroutines and delay to stop something from yielding. You’re rately gonna find yourself spawn()ing :WaitForChild() or wait(), you mainly need to spawn() while loops and stuff that will yield forever

print("hi")
while true do
    wait()
end
print("bye") --this will never be printed, unless the while loop is spawned

In your code, you spawned the Stepped event, it’s true that Steppedand its variants like RenderStepped keep on doing stuff forever, almost like while loops, although they don’t yield.

print("hi")
game:GetService("RunService").Stepped:Connect(function()
    --do stuff
end)
print("bye") --prints this straight away
1 Like

Oh I didn’t know that RunService functions don’t yield. Ok thank you.

1 Like

Events don’t yield, so yeah, but for your issue, the client can just send the CFrame and the Size, not much to be sent.

But even for the server to repeatedly render that for every user with a laser on, that’d reduce game performance I believe. Wouldn’t it be best if the server created the part, but then the client can render the physics for it which everyone would see?

The client could render the physics, but the Size won’t replicate, that’s the problem.

So you mean each frame the client sends the new length of ray to server, which updates the size, and then the client can control its position when it’s moved?

Yes, make sure the Network Owner is the player though, using BasePart:SetNetworkOwner()

Edit: I would still recommend the server doing all the work, laggy players will see a weird order of replication like that

Edit2: RemoteFunction:InvokeClient() isn’t recommended, as an exploiter could just remove the return line and would keep the server hanging forever, and the server should also wrap them in pcall so if the player leaves before the info is received by the server, the error won’t break the script

1 Like