Either shoot a ray downwards from the part and check if it hits a line, or check if the position of the part is within the area of the grid. You can find the documentation for Raycasts here.

The line is a texture, checking if its within the grid is easy however that will allow exploiters to place it anywhere within the grid which is not what I want

local function checkWallPlacement(data)
local position = data.pos
local gridData = GridsModule.getData(data.plr)[1]
local gridSize = gridData.gridPart.Size
local snapSize = gridData.tileSize
local grid = gridData.gridPart
local GridSize = {X = math.floor(grid.Size.X / snapSize), Z = math.floor(grid.Size.Z / snapSize)}
local Pivot = {X = GridSize.X - (GridSize.X / 2), Z = grid.Position.Z - (GridSize.Z / 2)}
local X = math.min(gridSize.X, math.max(0, math.floor((data.hitX - Pivot.X) / snapSize))) * snapSize
local Z = math.min(gridSize.Z, math.max(0, math.floor((data.hitZ- Pivot.Z) / snapSize))) * snapSize
print(X,Z)
if X % snapSize then
print("here")
end
if Z % snapSize then
print("here")
end
end

should I try something like this? not sure if my logic is right

You could just do this. Hereâ€™s an example where I checked if the X position is legit, you can use the same concept for the Z value.

local function checkWallPlacement(data)
local gridData = GridsModule.getData(data.plr)[1]
local gridSize = gridData.gridPart.Size
local snapSize = gridData.tileSize
-- Check if X is inside the grid, and if it's divisible by the snapSize then X is on the line texture
if data.hitX <= gridSize.X and data.hitX % snapSize == 0 then
print("X is legit")
end
end

I donâ€™t know what your data.hitX value is, but in this example I assumed it to be the Partâ€™s position on the X axis relative to the corner of the grid. So if your Part is in the center of the grid and the gridSize is 100 then data.hitX should be 50. Hereâ€™s how you can calculate this if youâ€™re not already doing this:

local function checkWallPlacement(data)
local gridData = GridsModule.getData(data.plr)[1]
local snapSize = gridData.tileSize
local gridPart = gridData.gridPart
local gridSize = gridData.gridPart.Size
local corner = gridPart.Position - gridSize / 2
data.hitX = data.pos.X - corner.X
data.hitZ = data.pos.Z - corner.Z
-- Check if X is inside the grid, and if it's divisible by the snapSize then X is on the line texture
if data.hitX <= gridSize.X and data.hitX % snapSize == 0 then
print("X is legit")
end
end

data.pos is the Position at the object/part, I tried this code and even when its on the grid it doesnâ€™t print "X is legitâ€™

The only problem I found is when the Part goes in a negative direction it still thinks that X is legit so supplement the if statement with X >= 0 like so:

if X >= 0 and X <= gridSize.X and X % snapSize == 0 then
print("X is legit")
end

Finally found a solution, instead of checking if its snappable on server I just sent raw data and snapped it on the server, thank you for your help though!