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)