How to replicate to server side

How would I go replicating this client sided code to server sided?

local armOffset = character:WaitForChild("UpperTorso").CFrame:Inverse() * character.RightUpperArm.CFrame

local armWeld = Instance.new("Weld")
armWeld.Part0 = character.UpperTorso
armWeld.Part1 = character.RightUpperArm
armWeld.Parent = character

RunService.RenderStepped:Connect(function()
	if tool.HaveOn.Value then -- // if player has tool equipped
		armWeld.Enabled = true
		local cframe = CFrame.new(character.UpperTorso.Position, mouse.Hit.Position) * CFrame.Angles(math.pi/2, 0, 0)
		armWeld.C0 = armOffset * character.UpperTorso.CFrame:toObjectSpace(cframe)
	else
		armWeld.Enabled = false
	end
end)

Result Client Sided:

Working attempt but only client sidedly

Failed attempt
-- // client sided
RunService.RenderStepped:Connect(function()
	if tool.HaveOn.Value then
		local cframe = CFrame.new(character.UpperTorso.Position, mouse.Hit.Position) * CFrame.Angles(math.pi/2, 0, 0)
		game.ReplicatedStorage.Events.FireExtinguisher.Hand:FireServer(cframe)
	end
end)

-- // server sided
game.ReplicatedStorage.Events.FireExtinguisher.Hand.OnServerEvent:Connect(function(plr,cframe)
	local character = plr.Character
	local armOffset = character:WaitForChild("UpperTorso").CFrame:Inverse() * character.RightUpperArm.CFrame
	
	local armWeld
	
	if not character:FindFirstChild("ArmWeld") then
		armWeld = Instance.new("Weld")
		armWeld.Part0 = character.UpperTorso
		armWeld.Part1 = character.RightUpperArm
		armWeld.Parent = character
		armWeld.Name = "ArmWeld"
	else
		armWeld = character.ArmWeld
	end
	
	armWeld.C0 = armOffset * character.UpperTorso.CFrame:toObjectSpace(cframe)
end)

Result:

1 Like

Firing a remote every frame is expensive.
Instead, have you tried simply giving the player network ownership of the part? This causes all of the position changes to be replicated to the server, and then all the other clients.

1 Like

Haven’t tried giving them network ownership, which part should be given network ownership? The UpperTorso, RightUpperArm or Handle (tool part)? Or all of them?

I believe all the character parts already have client network ownership, it’s probably just the handle.

2 Likes

Just tried, no luck. Client sees it but server isn’t picking it up

What code are you using for this? Are you using BasePart | Roblox Creator Documentation? If so, are you using it on the server or client? Have you tried using BasePart | Roblox Creator Documentation to see if there is a reason the client cannot control the part?

Obviously on the server, I haven’t tried CanSetNetworkOwnership

According to this post it like you’ll have to use remote events.

The issue isn’t with the part replicating but the Motor6D's.

I’ve tried, look above, its a weird result

That isn’t what you are supposed to do. Instead of setting the C0 value on the server you must instead do :FireAllClients to replicate the effect on all clients. They in turn will take the information you pass, calculate the cframe themselves, and then set the C0 value on their client.
Try this:

Local Script
local upperTorso = character:WaitForChild("UpperTorso")
local armOffset = upperTorso.CFrame:ToObjectSpace(character.RightUpperArm.CFrame)

local sendEvent = game:GetService("ReplicatedStorage").Events.FireExtinguisher.Hand -- the remote event for replicating your actions to other clients.
local recieveEvent = game.ReplicatedStorage.Events.FireExtinguisher.HandReplicate -- You should create another remote event for replicating other players hands.

local armWelds = {}

local armWeld = Instance.new("Weld")
armWeld.Enabled = tool.HaveOn.Value
armWeld.Part0 = character.UpperTorso
armWeld.Part1 = character.RightUpperArm
armWeld.Parent = character

-- Manipulate the arms client-side
local pi = math.pi
local function MoveArm(userId, userChar, pos)
	local weld
	if userId == nil then
		weld = armWeld
	else
		weld = armWelds[userId]

		if not weld then
			weld = Instance.new("Weld")
			weld.Enabled = true
			weld.Part0 = userChar.UpperTorso
			weld.Part1 = userChar.RightUpperArm
			weld.Parent = userChar
		end
	end

	local torsoFrame = userChar.UpperTorso.CFrame
	local targetFrame = CFrame.lookAt(torsoFrame.Position, pos) * CFrame.Angles(pi*0.5, 0, 0) -- 90 degrees
	weld.C0 = armOffset * torsoFrame:ToObjectSpace(targetFrame)
end

-- Replicate other players hands
recieveEvent.OnClientEvent:Connect(MoveArm)

local t = 0
local replicationRate = 0.05
game:GetService("RunService").RenderStepped:Connect(function(deltaTime : number)
	local enabled = tool.HaveOn.Value

	armWeld.Enabled = enabled
	if enabled then
		-- Visualize on this client
		MoveArm(nil,character,mouse.Hit.Position)

		-- Replicate to other clients
		t += deltaTime
		if t >= replicationRate then
			t %= replicationRate
			sendEvent:FireServer(character, mouse.Hit.Position)
		end
	end
end)
Server Script
local Players = game:GetService("Players")

local remote = game:GetService("ReplicatedStorage").Events.FireExtinguisher.Hand

local times = {}
local now = DateTime.now
Players.PlayerAdded:Connect(function(player)
	times[player] = {current=0,start=now().UnixTimestampMillis}
end)
Players.PlayerRemoving:Connect(function(player)
	times[player] = nil
end)

local replicationRate = 0.05 -- Only fires/replicates every 0.05 second(s) or 20 times a second
remote.OnServerEvent:Connect(function(player,...)
	local check = times[player] -- check on server

	if check then
		check.current += now().UnixTimestampMillis-check.start --  add the time (in seconds) in-between the current time and the start time

		if check.current >= replicationRate then
			check.current %= replicationRate
			for _,client in pairs(Players:GetPlayers()) do
				if player == client then continue end
				remote:FireClient(client,player.UserId,...)
			end
			check.start = now().UnixTimestampMillis -- reset cycle
		end
	end
end)

I understand why you are trying to replicate it on the server, but trust me, it will look and be a lot smoother if you do it on the client. Most, if not all, games make visuals like this visible to other clients, not by replicating it on the server, but by replicating it on the other clients. You can dm me if you have any questions. Also, you might want to create another remote event for when the player is no longer holding the part. That way you can disable to weld on the client. Should be simple enough though, so I’ll leave it to you.


P.S. - CFrame.new(Vector3 orgin, Vector3 lookAt) is deprecated and has been replaced with CFrame.lookAt.

2 Likes

Hey, thanks for taking the time towards writing this. Makes more sense, I’ve tested your code, no errors however the actual replication across clients isn’t showing


Have you tried using print statements in the MoveArm function to see whose userId, if any, is printing? You should also try this on the server just before the remote:FireClient. I think the problem is most likely due to the logic in the server code for counting the time in-between each fire. This isn’t to say that you shouldn’t do validation checks on the server, but I may have screwed it up. To test this try replacing the OnServerEvent function with this for now, just to see if it works.

remote.OnServerEvent:Connect(function(player,...)
	for _,client in pairs(Players:GetPlayers()) do
		if player == client then continue end
		remote:FireClient(client,player.UserId,...)
	end
end)
Server Side
local FireExtinguisherModule = require(script.Parent:WaitForChild("FireExtinguisherModule",3))
local Players = game.Players

local HandRemote = game:GetService("ReplicatedStorage").Events.FireExtinguisher.Hand

local times = {}
local now = DateTime.now

local replicationRate = 0.05 -- Only fires/replicates every 0.05 second(s) or 20 times a second
HandRemote.OnServerEvent:Connect(function(player,...)
	local check = times[player] -- check on server

	if check then
		check.current += now().UnixTimestampMillis-check.start --  add the time (in seconds) in-between the current time and the start time

		if check.current >= replicationRate then
			check.current %= replicationRate
			for _,client in pairs(Players:GetPlayers()) do
				if player == client then continue end
				HandRemote:FireClient(client,player.UserId,...)
			end
			check.start = now().UnixTimestampMillis -- reset cycle
		end
	end
end)

Players.PlayerAdded:Connect(function(plr)
	local character = plr.Character or plr.CharacterAdded:Wait()
	
	local back = plr.Backpack.Back:Clone()
	character.Humanoid:AddAccessory(back)
	
	times[plr] = {current=0,start=now().UnixTimestampMillis}
	
	FireExtinguisherModule.give(plr)
end)

Players.PlayerRemoving:Connect(function(plr)
	times[plr] = nil
end)
Local
local upperTorso = character:WaitForChild("UpperTorso")
local armOffset = upperTorso.CFrame:ToObjectSpace(character.RightUpperArm.CFrame)

local sendEvent = game:GetService("ReplicatedStorage").Events.FireExtinguisher.Hand
local recieveEvent = game.ReplicatedStorage.Events.FireExtinguisher.HandReplicate

local armWelds = {}

local armWeld = Instance.new("Weld")
armWeld.Enabled = tool.HaveOn.Value
armWeld.Part0 = character.UpperTorso
armWeld.Part1 = character.RightUpperArm
armWeld.Parent = character

local pi = math.pi
local function MoveArm(userId, userChar, pos)
	local weld
	if userId == nil then
		weld = armWeld
	else
		weld = armWelds[userId]

		if not weld then
			weld = Instance.new("Weld")
			weld.Enabled = true
			weld.Part0 = userChar.UpperTorso
			weld.Part1 = userChar.RightUpperArm
			weld.Parent = userChar
		end
	end

	local torsoFrame = userChar.UpperTorso.CFrame
	local targetFrame = CFrame.lookAt(torsoFrame.Position, pos) * CFrame.Angles(pi*0.5, 0, 0) -- 90 degrees
	weld.C0 = armOffset * torsoFrame:ToObjectSpace(targetFrame)
end

recieveEvent.OnClientEvent:Connect(MoveArm)

local t = 0
local replicationRate = 0.05
game:GetService("RunService").RenderStepped:Connect(function(deltaTime : number)
	local enabled = tool.HaveOn.Value

	armWeld.Enabled = enabled
	if enabled then
		MoveArm(nil,character,mouse.Hit.Position)
		
		t += deltaTime
		if t >= replicationRate then
			t %= replicationRate
			sendEvent:FireServer(character, mouse.Hit.Position)
		end
	end
end)

P.S I replaced that event with the new code above but nothing new

It’s because it isn’t actually receiving the info from other clients. In the server script you use:

However, in the local script it’s listening for the HandReplicate remote.

The receive event and the hand event are two separate events.

The recieveEvent is connected to the HandReplicate event and the sendEvent is connected to the Hand event. Now while I understand why you’d want to use the same event, it’s usually best to separate the remote calls into two separate RemoteEvent's. This is due to the limits that each remote has for the amount of calls it can make a second. I can’t actually find where they describe the limits due to them changing the documentation, but it relates to a fixed base value and then a multiplier for the amount of players in the server.

If you don’t have a RemoteEvent under game.ReplicatedStorage.Events.FireExtinguisher named “HandReplicate” then you should create one and name it “HandReplicate” (or whatever you want to name it, just make sure to change the name of the event in the code as well). Then your `ServerScript should be this:

local FireExtinguisherModule = require(script.Parent:WaitForChild("FireExtinguisherModule",3))
local Players = game.Players

local HandRemote = game:GetService("ReplicatedStorage").Events.FireExtinguisher.Hand
local ReplicateRemote = game.ReplicatedStorage.Events.FireExtinguisher.HandReplicate

local times = {}
local now = DateTime.now

local replicationRate = 0.05 -- Only fires/replicates every 0.05 second(s) or 20 times a second
HandRemote.OnServerEvent:Connect(function(player,...)
	local check = times[player] -- check on server

	if check then
		check.current += now().UnixTimestampMillis-check.start --  add the time (in seconds) in-between the current time and the start time

		if check.current >= replicationRate then
			check.current %= replicationRate
			for _,client in pairs(Players:GetPlayers()) do
				if player == client then continue end
				ReplicateRemote:FireClient(client,player.UserId,...)
			end
			check.start = now().UnixTimestampMillis -- reset cycle
		end
	end
end)

Players.PlayerAdded:Connect(function(plr)
	local character = plr.Character or plr.CharacterAdded:Wait()
	
	local back = plr.Backpack.Back:Clone()
	character.Humanoid:AddAccessory(back)
	
	times[plr] = {current=0,start=now().UnixTimestampMillis}
	
	FireExtinguisherModule.give(plr)
end)

Players.PlayerRemoving:Connect(function(plr)
	times[plr] = nil
end)


P.S. - On a more less important note, in the local script I accidentally misspelled receive as “recieve” for the recieveEvent variable. If you want to correct this than be my guest.
Also, if you get it working can you dm me a video of the results? While I’ve understood replication across clients for awhile now I’ve never actually tested it on my own. It would be cool to see the results.

2 Likes


Working bueno :+1:

Only had to make it so it resets the armOffset when the player is not holding the tool

2 Likes