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)
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.
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?
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.
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)
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.