Regardless of what you do on the client side, it will be exploitable. You will have to fix your server side, don’t just trust the client to give you what item they are picking up, you have to check if an item that was passed is actually on the ground or not. There are a lot of checks you can do on the server to verify if it’s actually a valid item, store the dropped items in a table, check if the table has the item when picking it up, use magnitude checks, etc…
Here’s what i do when the client requests an item:
function InventoryService.Client:RequestItem(player: Player, guid: string)
local item = workspace.DroppedItems:FindFirstChild(guid)
if self.Server.DroppedItems[guid] ~= nil and self.Server.DroppedItems[guid].Identifier and item then
local itemIdentifier = self.Server.DroppedItems[guid].Identifier
if DISTANCE_CHECK(player, item, 50) then
self.Server:addItem(player, itemIdentifier, tonumber(item:GetAttribute("Quantity")) or 1)
self.Server:removeDroppedItem(player, guid, tonumber(item:GetAttribute("Quantity")) or 1)
end
end
end
When dropping it, store it somewhere, so later on you can verify whether its valid or not.
function InventoryService:createDroppedItem(player: Player, itemIdentifier: string, quantity: number)
local itemModel = self.ItemService:GetItemModel(itemIdentifier)
if itemModel then
local droppedItem: BasePart = itemModel:Clone()
local guid = generateRandomItemIdentifier()
droppedItem.Name = guid
droppedItem:SetAttribute("Quantity", quantity)
droppedItem:SetAttribute("Name", itemIdentifier)
self.DroppedItems[guid] = {
Identifier = itemIdentifier,
Quantity = quantity,
}
local quadtree = Knit.GetService("QuadtreeService"):getQuadtree()
local playerCharacter = player.Character
local dropPosition = playerCharacter and playerCharacter.PrimaryPart.Position + Vector3.new(0, -1.5, 0)
or Vector3.new(0, 10, 0)
droppedItem.CanCollide = false
droppedItem.Anchored = true
if droppedItem:IsA("Model") then
droppedItem:SetPrimaryPartCFrame(CFrame.new(dropPosition))
if quadtree then
quadtree:insert({
position = droppedItem.PrimaryPart.Position,
reference = droppedItem,
})
print("Inserted into quadtree")
else
warn("Quadtree not found.")
end
else
droppedItem.Position = dropPosition
if quadtree then
quadtree:insert({
position = droppedItem.Position,
reference = droppedItem,
})
print("Inserted into quadtree")
else
warn("Quadtree not found.")
end
end
droppedItem.Parent = workspace.DroppedItems
self.Client.ItemDropped:FireAll(droppedItem, quantity)
return droppedItem
end
end
In my example i used a Quadtree, hence why im not passing where the item was dropped, and the client can only “see” the items around them in an x meter radius, a solid way to defend against exploiters
Lmk if you have any questions