Really bad ragdoll desync

Im using a ragdoll prediction system for my game and the desync is just so bad sometimes, especially when the ither player is jumping. Heres how it works:
the attacking player hits the victim. A remote is fired to the server. The victim’s client does their own physics. Since there is a period of freezing before the physics are applied on the other clients, the other clients predict the players ragdoll. It works fine most of the time, but when the ictim is in midair, the desync starts getting really bad.

External Media

(by the way the semi-transparent character is the real version and the solid one is the predicted character)

what ive tried:

-making increasng friction of the baseplate
-reseting velocity before the victim is knocked back

Heres the code for the client side knockback and the prediction:

knockEvent.OnClientEvent:Connect(function(kb, hitCFrame)
	
	local char = plr.Character
	char.HumanoidRootPart.CFrame = hitCFrame 
	
				char.HumanoidRootPart.AssemblyLinearVelocity = Vector3.new(0,0,0)
	char.HumanoidRootPart.AssemblyAngularVelocity = Vector3.new(0,0,0)
	char.HumanoidRootPart.Anchored  = true
	task.wait()
	char.HumanoidRootPart.Anchored  = false
	char.HumanoidRootPart.CFrame = hitCFrame 
	char.Torso.AssemblyLinearVelocity = kb
	local hum = char.Humanoid
	hum:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
	hum:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
	
end)
ragdollPredict.OnClientEvent:Connect(function(ragdollCharacter, knockback, originalTime, hitCFrame, plrVictim)
	if plr == plrVictim then return end
	ragdollCharacter.Archivable = true
	local delay = workspace:GetServerTimeNow() - originalTime
	local clonedchar = ragdollCharacter:Clone()
	

	
	clonedchar.Parent = workspace
	clonedchar.HumanoidRootPart.CFrame = hitCFrame

	for _,v in pairs(clonedchar:GetDescendants()) do 
	
		if v:IsA("BasePart") then
			v.CollisionGroup = "clientrigs"
		end
	end
	
	for _,v in pairs(ragdollCharacter:GetDescendants()) do 
		if (v:IsA("BasePart") or v:IsA("Decal")) and v.Name ~= "collisionpart" and v.Name ~= "HumanoidRootPart" then
			
			v.Transparency = 0.7
		
		end
	
	end


	ragdoll.Ragdoll(clonedchar, true)
	
	clonedchar.HumanoidRootPart.AssemblyLinearVelocity = Vector3.new(0,0,0)
	clonedchar.HumanoidRootPart.AssemblyAngularVelocity = Vector3.new(0,0,0)
	clonedchar.Torso.AssemblyLinearVelocity = Vector3.new(0,0,0)
	clonedchar.Torso.AssemblyAngularVelocity = Vector3.new(0,0,0)
	task.wait()
	clonedchar.HumanoidRootPart.CFrame = hitCFrame
	clonedchar.HumanoidRootPart.AssemblyLinearVelocity = knockback

	task.wait(2 + delay)

	clonedchar:Destroy()
	
	for _,v in pairs(ragdollCharacter:GetDescendants()) do 
		if (v:IsA("BasePart") or v:IsA("Decal")) and v.Name ~= "collisionpart" and v.Name ~= "HumanoidRootPart" then
			v.Transparency = 0

		end
		if v:IsA("BasePart") then
			v.CollisionGroup = "Default"
		end
	end

end)

you’re setting the torso velocity in one and humanoidrootpart velocity in the other. could that be what’s causing the problem?

no i switched it to this to test something before i was doing both humanoidrootpart and torso and the issue was still there

hmmm
that makes this even weirder

after further examination it could be that, in the player ragdoll, it anchors them prior to ragdolling, but in the predicted one it doesn’t. not sure if this could solve the problem but it’s worth trying.

i tried that and it didnt work. I think it has to do something with the random rotation of the rigs causing them to interact differently with the baseplate but i dont know how to couther that

yeah i was thinking it could be roblox physics jank
though i do think there may be a solution to this, i just don’t know what it is

It’s a bit janky but I thought I’d chime in with my solution to modern r15 ragdoll desync.

local Players = game:GetService('Players')
local function Ragdoll(Enabled)
	local Character = Players.LocalPlayer.Character
	local Humanoid = Character:FindFirstChildWhichIsA('Humanoid')
	local RootPart = Character:FindFirstChild('HumanoidRootPart')
	local Head = Character:FindFirstChild('Head')
	
	if not Enabled then -- Teleport the client character to where it is on the-
		if RootPart and Head then -- -server to prevent physical displacement
			RootPart.CFrame = CFrame.new(Head.Position + Vector3.new(0, 2, 0))
			RootPart.AssemblyLinearVelocity = Vector3.zero
			RootPart.AssemblyAngularVelocity = Vector3.zero
		end
	end

	for _, Joint in ipairs(Character:GetDescendants()) do -- Disable joints, fallback motors probably not helpful
		if Joint:IsA("AnimationConstraint") or (Joint:IsA("Motor6D") and Joint.Name ~= "RootJoint") then
			Joint.Enabled = not Enabled
		end
	end

    -- Apply state changes
	Humanoid:ChangeState(Enabled and Enum.HumanoidStateType.FallingDown or Enum.HumanoidStateType.GettingUp)
	
    -- Setting the CameraSubject to the head directly keeps it tracking on the servers idea rather than the clients
	Camera.CameraSubject = (Enabled and Head) and Head or Humanoid
end

Hope that helps.