Hello there!
I recently noticed a bug when working on my game. Say you have an assembly on the server with all the parts having the Massless
property enabled. When the assembly’s network owner gets set to the client and the client creates a new part & adds it to the assembly without setting it to Massless
too then the physics no longer gets replicated to the server.
The video below demonstrates this. A quick explanation of what’s going on in the video is that the two parts are exact copies of each other, with the only difference (other than the color) is that the code in blue one adds a little white part on the client to the assembly, which gets set to Massless
, but the code in the red one doesn’t set it to Massless
.
Reproduction
Create an unanchored part in workspace and set its Massless
property to true
.
Then create a server script (I added mine inside of the part) and with it set the network owner of the part to the client. I made mine like this:
local Players = game:GetService("Players")
local primary = script.Parent
-- A little countdown so the client could load in and get ready
for i = 3, 1, -1 do
print("Setting part network owner in "..i)
task.wait(1)
end
local player: Player = Players:GetPlayers()[1]
primary:SetNetworkOwner(player)
print("Part network owner set")
print("Firing remote event")
primary.RemoteEvent:FireAllClients()
Next up, create a client script (I added this one inside the part as well with RunContext set to Client
). Make it create another part and weld it to the original assembly & set the part to be Massless
as well. Then perform some physics-related things with said assembly. The code I made uses AlignPosition
, however as far as I’ve tested, the bug still occurs with other physics operations as well, such as :ApplyImpulse
. Anyways, here is the code I used:
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player: Player = Players.LocalPlayer
local character: Model = player.Character or player.CharacterAdded:Wait()
local part = script.Parent
print("Client is ready")
-- Create a new part, weld it and make the assembly move with AlignPosition
part:WaitForChild("RemoteEvent").OnClientEvent:Connect(function()
print("Client received event")
local clientPart = Instance.new("Part")
clientPart.Position = part.Position + Vector3.yAxis * 0.5
clientPart.Size = Vector3.one * 0.5
clientPart.Material = Enum.Material.Neon
clientPart.Color = Color3.new(1, 1, 1)
clientPart.Massless = true -- If this is set to false, physics doesn't replicate. If this is set to true, physics replicates
clientPart.Parent = part
local weldConstraint = Instance.new("WeldConstraint")
weldConstraint.Part0 = part
weldConstraint.Part1 = clientPart
weldConstraint.Parent = clientPart
local attachment = Instance.new("Attachment")
attachment.Parent = part
attachment.WorldPosition = clientPart.Position
local alignPosition = Instance.new("AlignPosition")
alignPosition.Mode = Enum.PositionAlignmentMode.OneAttachment
alignPosition.MaxForce = 100000
alignPosition.Responsiveness = 10
alignPosition.Attachment0 = attachment
alignPosition.Parent = part
RunService.Heartbeat:Connect(function() -- Every frame, set the position of the AlignPosition a little above the character
alignPosition.Position = character:GetPivot().Position + Vector3.new(0, 5, 0)
end)
end)
(The way I made it also requires a remote event inside the part that the server fires and the client receives)
So in total my set up looks like this
And that’s it.
I’ve already set all of this up in a simple place file and created a blue & red part, of which blue replicates and red does not. They are both set up as described above, the only important difference being that the red one has the client-sided part set to non-Massless
whereas the the blue one has it set to Massless
Bug test.rbxl (66.0 KB)
System Information
AMD Ryzen 5 5600G with Radeon Graphics
NVIDIA GeForce RTX 3060
Expected behavior
Once the client adds a non-Massless
part to the assembly, then the physics shouldn’t stop replicating from the client to the server.