File size: 4,886 Bytes
0712d5f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | -- src/ServerScriptService/DragManager.server.lua
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local DraggingConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("DraggingConfig"))
local DragEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("DragEvent")
local DropEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("DropEvent")
-- Store active dragging states
local activeDrags = {}
-- Validate if the object can be dragged (e.g. TreeSegments, Logs, Furniture)
local function canBeDragged(part)
return CollectionService:HasTag(part, "Draggable") or CollectionService:HasTag(part, "TreeSegment")
end
-- Cleanup function when a player drops an object or disconnects
local function dropObject(player)
local dragData = activeDrags[player.UserId]
if dragData then
-- Clean up attachments and constraints
if dragData.dragAttachment then dragData.dragAttachment:Destroy() end
if dragData.targetAttachment then dragData.targetAttachment:Destroy() end
if dragData.alignPos then dragData.alignPos:Destroy() end
if dragData.alignOri then dragData.alignOri:Destroy() end
-- Reset Network Ownership to Server (or Auto)
if dragData.part and dragData.part:IsDescendantOf(workspace) then
-- Optional: Add a small impulse so it doesn't just freeze mid-air
pcall(function()
dragData.part:SetNetworkOwner(nil)
end)
end
activeDrags[player.UserId] = nil
end
end
-- Handle Drag Event Request
local function onDragRequest(player, targetPart, hitPoint)
if not targetPart or not targetPart:IsA("BasePart") then return end
if not canBeDragged(targetPart) then return end
local char = player.Character
if not char or not char:FindFirstChild("HumanoidRootPart") then return end
-- Distance verification
local distance = (char.HumanoidRootPart.Position - targetPart.Position).Magnitude
if distance > DraggingConfig.MaxGrabDistance then return end
-- Ensure player isn't already dragging something
if activeDrags[player.UserId] then dropObject(player) end
-- Verify the part isn't already being dragged by someone else
for _, dragData in pairs(activeDrags) do
if dragData.part == targetPart then return end
end
-- Setup Attachments and Constraints
local partAttachment = Instance.new("Attachment")
partAttachment.Name = "DragAttachment"
partAttachment.CFrame = targetPart.CFrame:ToObjectSpace(CFrame.new(hitPoint))
partAttachment.Parent = targetPart
-- We create a proxy attachment in Terrain, managed by the client later, or just update it via Server
-- For Smoothest results, we give the Client network ownership and let local physics do the dragging, but the Constraints exist Server-Side.
local targetAttachment = Instance.new("Attachment")
targetAttachment.Name = "TargetAttachment_Player" .. player.UserId
-- Setting it to character's Head/Camera relative position on the server initially
targetAttachment.WorldCFrame = CFrame.new(char.Head.Position + char.Head.CFrame.LookVector * DraggingConfig.HoldDistance)
targetAttachment.Parent = workspace.Terrain
local alignPos = Instance.new("AlignPosition")
alignPos.Attachment0 = partAttachment
alignPos.Attachment1 = targetAttachment
alignPos.MaxForce = DraggingConfig.AlignPositionMaxForce
alignPos.Responsiveness = DraggingConfig.AlignPositionResponsiveness
alignPos.Parent = targetPart
local alignOri = Instance.new("AlignOrientation")
alignOri.Attachment0 = partAttachment
alignOri.Attachment1 = targetAttachment
alignOri.MaxTorque = DraggingConfig.AlignOrientationMaxTorque
alignOri.Responsiveness = DraggingConfig.AlignOrientationResponsiveness
alignOri.Parent = targetPart
-- Grant Network Ownership to the Player to eliminate latency rubber-banding
pcall(function()
targetPart:SetNetworkOwner(player)
end)
-- Save the state
activeDrags[player.UserId] = {
part = targetPart,
dragAttachment = partAttachment,
targetAttachment = targetAttachment,
alignPos = alignPos,
alignOri = alignOri
}
end
-- Handle Drop Event Request
local function onDropRequest(player)
dropObject(player)
end
-- Players leaving cleanup
Players.PlayerRemoving:Connect(function(player)
dropObject(player)
end)
DragEvent.OnServerEvent:Connect(onDragRequest)
DropEvent.OnServerEvent:Connect(onDropRequest)
|