Parts that are welded and have their AssemblyAngularVelocity changed will not replicate to current players but are replicated to new joins

Uncopylocked Repro Place: Evil Weld Game - Roblox

Existing player:

New player:
image-1

Video:

Offending code:

	local Weld = Instance.new("Weld")
	Weld.Part0 = HumanoidRootPart
	Weld.Part1 = EvilPart
	Weld.C0 = CFrame.new()
	Weld.Parent = HumanoidRootPart
	Weld.Name = "EmoteWeld"
	
	task.wait(5)
	
	task.defer(function()
		task.wait()
		HumanoidRootPart.AssemblyAngularVelocity = Vector3.new(10, 10, 10)
		Weld:Destroy()
		HumanoidRootPart.AssemblyAngularVelocity = Vector3.zero
	end)
6 Likes

Can you provide a detailed list of steps to reproduce the problem using your repro place? I see the same displayed results for both clients when testing with multiple players, so I’m trying to understand what steps are necessary to induce the problem.

Ideally something like:

  1. Player1 joins
  2. Press weld button on Player1 client
  3. Player2 joins
  4. Player 1 and Player2 see different velocity values

As much detail you can provide on how to reproduce and what exactly doesn’t have the expected value or is missing would be helpful.

Also, when did this issue start occurring? Do you think it is behavior that changed recently?

1 Like

In the video what I did was

  1. Player1 joined
  2. Player1 pressed the teleport button then pressed the weld button
  3. After Player1 is unwelded the ma
  4. Player2 joins after Player1 is unwelded
  5. Player2 will teleport to the part and the part will be streamed in (observe the No Weld Part while Player2 is too far away)
  6. Player2 will have the server’s velocity and will start spinning

Player1 POV: https://youtu.be/dgNjq2Jy_a4
Player2 POV: https://youtu.be/22CDP-nryGs

Earliest bug report we have of this issue is 16/10/2023 which was 3 days after we added the item that caused us to start encountering this bug. It has likely been around for a while. As an important note if streaming is disabled the server velocity will still be changed but the client velocity will be 0,0,0 for new players and existing players.

I’m sorry, I’m still having trouble following what is not replicating. Which part isn’t replicating?

I’m a bit puzzled by your code setting the velocity and then immediately setting the velocity back to zero. Does the problem not occur if you don’t reset the velocity to zero?

FYI, I would expect you would get this same behavior if you test locally in Studio and spawn a few players:
image

Or start with one player and then start a second player. You shouldn’t need to ask other people to join I would hope.

Since streaming is enabled the part that the character is welded to will not be replicated until your character comes within range of that part. Observe the “No Weld Part”, “Client: x,x,x” and “Server: x,x,x” values in recorings.

The code does not need to immediately set the velocity back to zero, it is only done this way to make testing easier. It sets the AssemblyLinearVelocity which is different to the actual Velocity.

Additionally here is a video of me testing it in studio only relying on the part not being streamed in:

Me and @Unlimited_Objects were able to reproduce the issue in the test place linked in OP’s post.

First Player POV: https://youtu.be/pQ5oNWj3tZU
Second Player POV: https://youtu.be/jwGhC0jai8s

1 Like

So, unfortunately for now this is a difficult case to resolve.

The Roblox replication system assumes that all property changes are independent from each other, when sending them over the network, so that they can be re-shuffled for optimal perf/bandwidth use.

This assumption is broken with certain physics/position properties like “AssemblyAngularVelocity”, because the side-effects of this property being called depends on the state of your assemblies. When you set that property AND break a weld, if those changes are delivered in the same frame, they may get shuffled such that the velocity is applied on the “wrong” assembly state.

For example if on server you have [ Part A ]--weld--[ Part B ] and you call

PartA.AssemblyAngularVelocity = X
weld:Destroy()
PartA.AssemblyAngularVelocity = Y

On the server you would see PartB having angular velocity X while PartA having angular velocity Y… but the Client MAY (not guaranteed) apply angular velocity Y on both PartA and PartB, because when it gets applied it still thinks both objects are the same assembly.

This is because the Replication system sees the first call to PartA.AssemblyAngularVelocity = X as redundant and simply replaces it with PartA.AssemblyAngularVelocity = Y in place, which causes the Client to receive an update that looks like this:

PartA.AssemblyAngularVelocity = Y
weld:Destroy()

We’re working on a long-term project to clean up some of these replication caveats, but it would be very difficult to fix this in the short term, so I won’t have a solution for you in the short term for a while.

If I understand your problem correctly:
A possible workaround for your issue you can use RemoteEvent or RemoteFunction to force these changes to happen atomically.

So when you do the initial set of calls, a remote event first

setVelocitiesBreakWeld:FireClients()
PartA.AssemblyAngularVelocity = X
weld:Destroy()
PartA.AssemblyAngularVelocity = Y

and on the Clients basically make sure your remote event function does the exact set of operations you need in the right order.

setVelocitiesBreakWeld.OnClientEvent:connect(function()
    PartA.AssemblyAngularVelocity = X
    weld:Destroy()
    PartA.AssemblyAngularVelocity = Y
end)

This might do redundant work, but it will force everything into the right state “atomically”.

Alternatively, waiting a frame between

PartA.AssemblyAngularVelocity = X
weld:Destroy()

and

PartA.AssemblyAngularVelocity = Y

should make this less likely to happen but doesn’t guarantee it during congested network situations.

Does that make sense? And did I describe the issue correctly? Let me know if I misunderstood something.

2 Likes

If I understand your problem correctly

For RoCitizens we had this problem because in our game the bomb item would set a velocity before breaking the emoting weld which is how we started to encounter this issue. We fixed it by simply breaking the weld before modifying the player velocity instead. We have it fixed on our side but we reported it since it didn’t feel like intended behaviour of the engine.

Does that make sense? And did I describe the issue correctly? Let me know if I misunderstood something.

Yes that does make sense. Thank you for explaining the issue.