If I recall correctly, you can’t store a TerrainRegion from game.Workspace.Terrain:CopyRegion() in something like a Datastore. So, I made a module to do so.
--Configuration
local ReadSpeed = 10 --Speed of how fast it reads the terrain and makes the data (Should be kept low like 1-15)
local InstantRead = false --Use wait()s in reading terrain if false. DO NOT SET TO TRUE IF USING LARGE REGIONS
local WriteSpeed = 250 --Speed of how fast it reads the data and makes the terrain (Can be 1-1000 with no issues)
local InstantWrite = false --Use waits()s in reading data if false. DO NOT SET TO TRUE IF USING LARGE REGIONS
--Mainscript
local Terrain = game.Workspace.Terrain
local Enums = {
Materials = {
["Enum.CellMaterial.Grass"] = 1,
["Enum.CellMaterial.Sand"] = 2,
["Enum.CellMaterial.Brick"] = 3,
["Enum.CellMaterial.Granite"] = 4,
["Enum.CellMaterial.Asphalt"] = 5,
["Enum.CellMaterial.Iron"] = 6,
["Enum.CellMaterial.Aluminum"] = 7,
["Enum.CellMaterial.Gold"] = 8,
["Enum.CellMaterial.WoodPlank"] = 9,
["Enum.CellMaterial.WoodLog"] = 10,
["Enum.CellMaterial.Gravel"] = 11,
["Enum.CellMaterial.CinderBlock"] = 12,
["Enum.CellMaterial.MossyStone"] = 13,
["Enum.CellMaterial.Cement"] = 14,
["Enum.CellMaterial.RedPlastic"] = 15,
["Enum.CellMaterial.BluePlastic"] = 16,
["Enum.CellMaterial.Water"] = 17,
},
BlockTypes = {
["Enum.CellBlock.Solid"] = 0,
["Enum.CellBlock.VerticalWedge"] = 1,
["Enum.CellBlock.CornerWedge"] = 2,
["Enum.CellBlock.InverseCornerWedge"] = 3,
["Enum.CellBlock.HorizontalWedge"] = 4,
},
Orientations = {
["Enum.CellOrientation.NegZ"] = 0,
["Enum.CellOrientation.X"] = 1,
["Enum.CellOrientation.Z"] = 2,
["Enum.CellOrientation.NegX"] = 3,
}
}
local function GetRegionSize(Region)
return Region.Max.X - Region.Min.X,Region.Max.Y - Region.Min.Y,Region.Max.Z - Region.Min.Z
end
local Module = {}
function Module:WriteTerrainData(Region)
local SizeX,SizeY,SizeZ = GetRegionSize(Region)
local grid = {}
local function setVal(a,b,c,x,y,z)
local aDim = grid[a] or {}
grid[a] = aDim
local bDim = aDim[b] or {}
aDim[b] = bDim
local cDim = bDim[c] or {}
bDim[c] = cDim
local xDim = cDim[x] or {}
cDim[x] = xDim
local yDim = xDim[y] or {}
xDim[y] = yDim
yDim[z] = z
end
for i = 1, SizeX + 1 do
for j = 1, SizeZ + 1 do
for k = 1, SizeY + 1 do
local M,B,O = Terrain:GetCell(i + Region.Min.X - 1,k + Region.Min.Y - 1,j + Region.Min.Z - 1)
if tostring(M) ~= "Enum.CellMaterial.Empty" then
setVal(Enums.Materials[tostring(M)],Enums.BlockTypes[tostring(B)],Enums.Orientations[tostring(O)],i - 1,k - 1,j - 1)
end
end
end
if InstantRead == false then if math.floor(i/ReadSpeed)*ReadSpeed == i then wait() end end
end
return {Data=grid,SizeX = SizeX,SizeY = SizeY,SizeZ = SizeZ}
end
function Module:ReadTerrainData(DataTable,Corner,Erase)
local Data,SizeX,SizeY,SizeZ = DataTable.Data,DataTable.SizeX,DataTable.SizeY,DataTable.SizeZ
if Erase == true then
Terrain:SetCells(Region3int16.new(Corner,Corner + Vector3int16.new(SizeX - 1,SizeY - 1,SizeZ - 1)),0,0,0)
end
local i = 0
for Material,Blocks in pairs(Data) do
for Block,Orientations in pairs(Blocks) do
for Orientation,Xs in pairs(Orientations) do
for X,Ys in pairs(Xs) do
i = i + 1
for Y,Zs in pairs(Ys) do
for Z,_ in pairs(Zs) do
Terrain:SetCell(X + Corner.X,Y + Corner.Y,Z + Corner.Z,Material,Block,Orientation)
end
end
if InstantWrite == false then if math.floor(i/WriteSpeed)*WriteSpeed == i then wait() end end
end
end
end
end
end
return Module