Client-Sided BallSocketConstraint Not Holding

I have been working on a character throwing system, and there is a potential for a player to throw many characters at once, so I’ve decided to only render the character on the client side to conserve resources. The client-sided character being thrown is welded to its own server-side root part. This part has the AssemblyLinearVelocity property changed to create the throw.

The parts in the client-sided character model are held together with BallSocketConstraints to create a ragdoll effect, with the exception of the head, upper torso, and lower torso, which are held by Motor6Ds. When the velocity is applied to the server part, the parts held by Motor6Ds actually hold together and are thrown. The limbs, however, are left behind and the BallSocketConstraint ceases to function. There is some sort of weird stretching effect.

Here are two videos of the issue with different camera angles:


I’ve attached a few snippets of code below:

Client-sided ragdoll function:

local function ragdoll(friendModel)
	for i, v in pairs(friendModel:GetDescendants()) do
		if v:IsA("Motor6D") and v.Name ~= "Neck" and v.Name ~= "Waist" and v.Name ~= "Root" then
			local Constraint = Instance.new("BallSocketConstraint")
			Constraint.Name = v.Name
			Constraint.Radius = 1
			Constraint.LimitsEnabled = true
			Constraint.TwistLimitsEnabled = true
			Constraint.TwistLowerAngle = -45
			Constraint.TwistUpperAngle = 90
			Constraint.MaxFrictionTorque = 3
			
			local attachment1 = v.Part1:FindFirstChild(v.Name.."Attachment") or v.Part1:FindFirstChild(v.Name.."RigAttachment")
			local attachment0 = v.Part0:FindFirstChild(v.Name.."Attachment") or v.Part0:FindFirstChild(v.Name.."RigAttachment")
			
			if attachment1 and attachment0 then
				Constraint.Attachment1 = attachment1
				Constraint.Attachment0 = attachment0
				Constraint.Parent = v.Parent
				v.Enabled = false
			end
		elseif v:IsA("BasePart") then
			v.Massless = true
			v.CollisionGroup = "Friends"
		end			
	end
end

Server-sided throw script:

HRP.Anchored = true
HRP.Orientation = orientation
	
local animTrack = playAnim(plr, "rbxassetid://90109218766018")
animTrack:GetMarkerReachedSignal("StartThrow"):Wait()
	
local animSpeed = BV/startBV
if animSpeed > maxAnimSpeed then
	animSpeed = maxAnimSpeed
end
animTrack:AdjustSpeed(animSpeed)
	
local friendFolder = rigFolders:WaitForChild(plr.Name)
local friendModels = friendFolder:GetChildren()
	
for i, hitbox in pairs(friendModels) do -- Each hitbox is a server-sided character root part
	hitbox.Weld.Enabled = false
	hitbox.AssemblyLinearVelocity = velocity
	
	if i == #friendModels then
		remotes.viewFriend:FireClient(plr, true, hitbox)
	end
end
1 Like

So, I tried making the actual throw client-sided and just detaching the character from the server-side root part, and it works fine now. However, I would love for the throw to remain server-sided so that I can track collisions with the root part. If I make the throw completely client-sided, I cannot detect any collisions on the server. Does anyone have any idea of a way I could make this work?