I am trying to create a physics-based item dragging system like the one in Lumber Tycoon 2. I have already made an unsecure, fully client-controlled system that works. The issue I’m having is security. I am using align positions and orientations to move the part, and want there to be a max force, torque, velocity, etc. Currently, the client controls everything, so someone could easily change the values, or just teleport the part wherever they want. I have a server script that automatically sets the part’s networkownership to the player. How can I secure this so players cannot change the physics limits applied to the part, and cannot teleport the part when they have ownership?
Here is my current code
local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlayersService = game:GetService("Players")
local RunService = game:GetService("RunService")
local UIS = game:GetService("UserInputService")
local Player = PlayersService.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid") :: Humanoid
local RootPart = Character:WaitForChild("HumanoidRootPart") :: BasePart
local Camera = workspace.CurrentCamera
local Mouse = Player:GetMouse()
local Responsiveness = 60
local MaxForce = 60000
local MaxTorque = 30000
local MaxVelocity = 40
local MaxAngularVelocity = 20
local MaxGrabDistance = 12
local MaxHoldDistance = 8
local MinHoldDistance = 3
local HoldingDistance = 6
local RotationMultiplier = 4
local DistanceChangeMultiplier = 2
local DraggableObjectTag = "Draggable"
local Dragging = false
local DraggingPart = nil
local Constraints = {
AlignPosition = nil,
AlignOrientation = nil,
Attachment = nil
}
local Keybinds = {
Rotate = Enum.KeyCode.R,
IncreaseDistance = Enum.KeyCode.X,
DecreaseDistance = Enum.KeyCode.Z,
Throw = Enum.KeyCode.F
}
local RotationOffset = CFrame.new(0, 0, 0)
local CFrameConnection = nil
local function HoveringDraggablePart()
local Part = Mouse.Target
if not Part or not CollectionService:HasTag(Part, DraggableObjectTag) then return end
local Distance = (Part.Position - RootPart.Position).Magnitude
if Distance > MaxGrabDistance then return end
return Part, Distance, Mouse.Hit.Position
end
local function BuildConstraints(grabPos)
local Attachment = Instance.new("Attachment")
Attachment.Parent = DraggingPart
Attachment.WorldPosition = grabPos
local AlignPosition = Instance.new("AlignPosition")
AlignPosition.Parent = DraggingPart
AlignPosition.Mode = Enum.PositionAlignmentMode.OneAttachment
AlignPosition.Attachment0 = Attachment
AlignPosition.MaxForce = MaxForce
AlignPosition.MaxVelocity = MaxVelocity
AlignPosition.Responsiveness = Responsiveness
local AlignOrientation = Instance.new("AlignOrientation")
AlignOrientation.Parent = DraggingPart
AlignOrientation.Mode = Enum.OrientationAlignmentMode.OneAttachment
AlignOrientation.Attachment0 = Attachment
AlignOrientation.MaxTorque = MaxTorque
AlignOrientation.MaxAngularVelocity = MaxAngularVelocity
AlignOrientation.Responsiveness = Responsiveness
Constraints.Attachment = Attachment
Constraints.AlignPosition = AlignPosition
Constraints.AlignOrientation = AlignOrientation
end
local function DestroyConstraints()
for _, constraint in Constraints do
if constraint then constraint:Destroy() end
end
end
local function UpdateDraggingCFrame()
if UIS:IsKeyDown(Keybinds.IncreaseDistance) then
HoldingDistance = math.clamp(HoldingDistance + 0.1 * DistanceChangeMultiplier, MinHoldDistance, MaxHoldDistance)
end
if UIS:IsKeyDown(Keybinds.DecreaseDistance) then
HoldingDistance = math.clamp(HoldingDistance - 0.1 * DistanceChangeMultiplier, MinHoldDistance, MaxHoldDistance)
end
if UIS:IsKeyDown(Keybinds.Rotate) then
Humanoid.WalkSpeed = 0
local RotationDelta = CFrame.new()
if UIS:IsKeyDown(Enum.KeyCode.W) then
RotationDelta *= CFrame.Angles(-0.01 * RotationMultiplier, 0, 0)
end
if UIS:IsKeyDown(Enum.KeyCode.S) then
RotationDelta *= CFrame.Angles(0.01 * RotationMultiplier, 0, 0)
end
if UIS:IsKeyDown(Enum.KeyCode.A) then
RotationDelta *= CFrame.Angles(0, 0.01 * RotationMultiplier, 0)
end
if UIS:IsKeyDown(Enum.KeyCode.D) then
RotationDelta *= CFrame.Angles(0, -0.01 * RotationMultiplier, 0)
end
if UIS:IsKeyDown(Enum.KeyCode.E) then
RotationDelta *= CFrame.Angles(0, 0, -0.01 * RotationMultiplier)
end
if UIS:IsKeyDown(Enum.KeyCode.Q) then
RotationDelta *= CFrame.Angles(0, 0, 0.01 * RotationMultiplier)
end
RotationOffset *= RotationDelta
else
if Humanoid.WalkSpeed == 0 then
Humanoid.WalkSpeed = 16
end
end
Mouse.TargetFilter = DraggingPart
local Direction = CFrame.lookAt(DraggingPart.Position, Vector3.new(RootPart.Position.X, DraggingPart.Position.Y, RootPart.Position.Z))
local DragVector = CFrame.new((RootPart.CFrame * CFrame.new(0, 2, 0).Position), Mouse.Hit.Position)
local Position = (DragVector + (DragVector.LookVector * HoldingDistance)).Position
local Rotation = Direction * RotationOffset
Constraints.AlignPosition.Position = Position
Constraints.AlignOrientation.CFrame = Rotation
Mouse.TargetFilter = nil
end
local function GrabPart(part, distance, grabPosition)
if Dragging then return end
Dragging = true
HoldingDistance = math.clamp(distance, MinHoldDistance, MaxHoldDistance)
DraggingPart = part
RotationOffset = CFrame.lookAt(part.Position, Vector3.new(RootPart.Position.X, part.Position.Y, RootPart.Position.Z)):ToObjectSpace(part.CFrame).Rotation
BuildConstraints(grabPosition)
CFrameConnection = RunService.RenderStepped:Connect(UpdateDraggingCFrame)
end
local function ReleasePart()
if not Dragging then return end
Dragging = false
DraggingPart = nil
DestroyConstraints()
CFrameConnection:Disconnect()
end
Mouse.Button1Down:Connect(function()
local Part, Distance, GrabPosition = HoveringDraggablePart()
print(Part, Distance, GrabPosition)
if Part then
GrabPart(Part, Distance, GrabPosition)
end
end)
Mouse.Button1Up:Connect(function()
if Dragging then
ReleasePart()
end
end)