Make RenderStepped function appear on server

Hello developers,

I’m trying to replicate a part’s CFrame change from a RenderStepped loop to appear on the server/all clients. My problem is that I can’t use RenderStepped in the server so the server can’t see the changes and the CFrame changes only appear for me, but not for the server. I’ve also tried a while loop, but the problem with that is that the changes don’t appear to neither me nor the server. Is there any solution to this?

https://gyazo.com/08269b13a1c2d9b51a1d1270cd04b093

Code:

--//Module script
beams.FireBeam = function()
	Remotes.StartBeam:FireServer(RootPart, Mouse.Hit)
			
	RunService.RenderStepped:Connect(function()
		Origin.CFrame = ProtonGun.Handle.StreamPos.CFrame
		End.CFrame = Mouse.Hit
	end)
end

beams.StopBeam = function()
	Remotes.StopBeam:FireServer()
end

--//Client
local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()

local BeamFunctions = require(script.Parent:WaitForChild("BeamFunctions"))

Mouse.Button1Down:Connect(function()
	BeamFunctions.FireBeam()
end)

Mouse.Button1Up:Connect(function()
	BeamFunctions.StopBeam()
end)

--//Server
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Origin = ReplicatedStorage.Origin
local End = ReplicatedStorage.End
local ProtonGun = game:GetService("StarterPack"):WaitForChild("ProtonGun")

local isProtonFiring = false

function startBeam(player, RootPart, MouseHit)
	Origin.Parent = RootPart
	End.Parent = game.Workspace
	
	isFiringProton = true
	
	--[[while isFiringProton == true do --//This is the while loop I tried
		Origin.CFrame = ProtonGun.Handle.StreamPos.CFrame
		End.CFrame = MouseHit
		
		task.wait()
	end]]
end

Remotes.StopBeam.OnServerEvent:Connect(function()
	Origin.Parent = ReplicatedStorage
	End.Parent = ReplicatedStorage
	
	isFiringProton = false
end)

Remotes.StartBeam.OnServerEvent:Connect(startBeam)

I honestly don’t see why you trying to use a while loop on the server at all. The client is firing all of the needed parameters to the server each update, so why not just do it without the while loop?

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Origin = ReplicatedStorage.Origin
local End = ReplicatedStorage.End

local isProtonFiring = false

function startBeam(player, RootPart, MouseHit)
	Origin.Parent = RootPart
	End.Parent = game.Workspace
	
	isFiringProton = true
	
	Origin.CFrame = ProtonGun.Handle.StreamPos.CFrame
	End.CFrame = MouseHit
end

Remotes.StopBeam.OnServerEvent:Connect(function()
	Origin.Parent = ReplicatedStorage
	End.Parent = ReplicatedStorage
	
	isFiringProton = false
end)

Also, you will need to define ProtonGun within the server script.

I just seem to get the same result

I had this idea:
Create a client-replicated part sometime before shooting the beam.
Tell server to tell other clients to begin shooting the beam as well.
Have the other clients generate a beam from RootPart to ReplicatedPart.
Don’t generate the beam on the server.

2 Likes

why not just use the heartbeat function on the server?

2 Likes

I tried using it, but it only changes on the first click. I don’t know if I’m using correctly, so can you explain how I can use it in my code?

1 Like

im not experienced in runservice specifically, but heartbeat is just a runservice loop you could use on the server. Its really fast but not as fast as render stepped.

The heartbeat function fires every frame. If your game is running on 40 fps heartbeat will run 40 times per second and the argument (parameter) would be 1/40th of a second

thats the example given on the website but i simplified it. Heres the website:
https://developer.roblox.com/en-us/api-reference/event/RunService/Heartbeat

3 Likes

Update:

I got the End part of the beam to work on the server and client! Only problem is now the Origin part (where the ProtonGun is) doesn’t follow me neither on client nor on the server.

https://gyazo.com/fdc0ac247206e8619384206cf050e471

Another update:

I got the Origin following me on the server and client. Only problem is that on client, I see the Origin’s part position change late, while the server is on track and it looks fine. Is there a way so I can make it even on both the client and server?

https://gyazo.com/ab0488119315b37975f9388646f8eac2

That’s why I said to generate the beam on the clients. That or generate it on the client who is firing the beam and have it replicate to the server and then to other clients.

The player firing the beam is replicating the character to the server and then the server is taking that replication and generating the beam which then replicates back to the player. This creates the moment of lag you are seeing.

1 Like

Can you tell how I exactly do that? The code I’ve tried doesn’t seem to work. There’s no errors

https://gyazo.com/06a4ee146c7fb719408d547515051713

local mouseHit
local Origin
local End

Remotes.StartBeam.OnServerEvent:Connect(function(player, originPart, endPart, hit, rootpart)
	newOrigin = originPart:Clone()
    newOrigin.Parent = rootpart
	Origin = true
	newEnd = endPart:Clone()
    newEnd.Parent = game.Workspace
	End = true
	
	mouseHit = hit
end)

Remotes.StopBeam.OnServerEvent:Connect(function()
	newOrigin:Destroy()
	Origin = false
	newEnd:Destroy()
	End = false
	
	mouseHit = nil
end)

RunService.Heartbeat:Connect(function()
	if mouseHit ~= nil then
		newOrigin.CFrame = ProtonGun.Handle.StreamPos.CFrame
		newEnd.CFrame = mouseHit
	end
end)

Edit: So I got the client part working smoothly, I just can’t figure out the server part

Remotes.StartBeam.OnServerEvent:Connect(function(player, originPart, endPart)
  Remotes.StartBeam:FireAllClients(originPart, endPart, player.UserId)
end)
Remotes.StopBeam.OnServerEvent:Connect(function(player)
  Remotes.StopBeam:FireAllClients(player.UserId)
end)

then on the client:

local connections = {}
local rs = game:GetService("RunService")
Remotes.StartBeam.OnClientEvent:Connect(function(originPart, endPart, id)
  connections[id] = rs.RenderStepped:Connect(function(dt) -- we use RenderStepped so it looks smooth while animating
    -- generate beam from originPart to endPart
  end)
end)
Remotes.StopBeam.OnClientEvent:Connect(function(id)
  if connections[id] then connections[id]:Disconnect(); connections[id] = nil end
end)
-- userinputservice or something to trigger beam
-- also define mouse
  Remotes.StartBeam:FireServer(originPart, endPart)
  rs.Stepped:Connect(function(time, dt) -- we use Stepped because it changes with the
    -- character, which is the physics update.
    endPart.CFrame = mouse.Hit -- use mouse here so the endPart is being moved to where the mouse is
    --, which will replicate to the server along with your character (so there's no weird lag offsetting the beam)
  end)
1 Like

I’m sorry sometimes I feel like speaking code is easier to understand. The basic idea is that the server simply tells the other clients when to start or stop generating the beam. And then the clients have to generate the beam and the client shooting the beam has to tell everybody somehow where the end/origin is supposed to be. Since the originPart is basically attached to the character, it will be replicated easily. But you have to manually move the mouse’s position to get the endPart to replicate that position.
You could alternatively spam a remote event with the mouse’s position.

1 Like

It doesn’t seem to be working. The endPart updates on the client, but not on the server, and the originPart doesn’t update at all on the client nor server. And the endPart and originPart don’t appear on the server.

Here’s my code (contains server, client, and module script):

--//Client
Remotes.StartBeam.OnClientEvent:Connect(function(originPart, endPart, id)
	connections[id] = RunService.RenderStepped:Connect(function(dt)
		BeamFunctions.FireBeam()
	end)
end)

Remotes.StopBeam.OnClientEvent:Connect(function(id)
	if connections[id] then
		BeamFunctions.StopBeam()
		
		connections[id]:Disconnect()
		connections[id] = nil
	end
end)

Mouse.Button1Down:Connect(function()
	Remotes.StartBeam:FireServer(Origin, End)
	
	RunService.Stepped:Connect(function(time, dt)
		End.CFrame = Mouse.Hit
	end)
end)

Mouse.Button1Up:Connect(function()
	Remotes.StopBeam:FireServer()
end)

--//Server
Remotes.StartBeam.OnServerEvent:Connect(function(player, originPart, endPart)
	Remotes.StartBeam:FireAllClients(originPart, endPart, player.UserId)
end)

Remotes.StopBeam.OnServerEvent:Connect(function(player)
	Remotes.StopBeam:FireAllClients(player.UserId)
end)

--//ModuleScript
beams.StopBeam = function()
	Origin.Parent = ReplicatedStorage
	End.Parent = ReplicatedStorage
end

beams.FireBeam = function()
	Origin.Parent = RootPart
	End.Parent = game.Workspace
end

If it a replication issue then it’s because of network ownership. You have to set the network ownership of the parts to the client changing them. You have to do this from the server. Also the parts can’t be anchored when you do this.

https://developer.roblox.com/en-us/api-reference/function/BasePart/SetNetworkOwner

1 Like

Where would I put this? I know SetNetworkOwner can only be used on the server, but where do I put it in my server script?

I’m marking this as solved since I’ve found something that works. Thank you all!