I’m trying to make a server script where it generates blocks and what I have now is not saving the chunks, I am trying to make it save and generate ‘forever’. I have been trying multiple different strategies such as datastore but nothing quite fixed that. Once a part inside one of the folders is added or destroyed from the player destroying it or placing it, it should save that chunk once they leave the area and come back it should stay the same as it was. The generation should be like Minecraft, it saves and it has chunks. Here is the server script below:
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local BlocksFolder = ServerStorage:WaitForChild("BlocksFolder")
local GrassBlock = BlocksFolder:WaitForChild("GrassBlock")
local WorkspaceBlocks = workspace:FindFirstChild("Blocks") or Instance.new("Folder", workspace)
WorkspaceBlocks.Name = "Blocks"
local CHUNK_SIZE = 1
local RENDER_DISTANCE = 25
local activeChunks = {}
local function findBlockAtPosition(position)
local roundedPos = Vector3.new(math.round(position.X), math.round(position.Y), math.round(position.Z))
local chunkX = math.floor(roundedPos.X / (CHUNK_SIZE * 3.5))
local chunkZ = math.floor(roundedPos.Z / (CHUNK_SIZE * 3.5))
local chunkFolder = activeChunks[chunkX] and activeChunks[chunkX][chunkZ]
if chunkFolder then
for _, child in ipairs(chunkFolder:GetChildren()) do
if (child.Position - position).Magnitude < 1 then
return child
end
end
end
return nil
end
local function updateBlockGUIs(block)
local neighbors = {
Vector3.new(block.Position.X + 3.5, 0, block.Position.Z),
Vector3.new(block.Position.X - 3.5, 0, block.Position.Z),
Vector3.new(block.Position.X, 0, block.Position.Z + 3.5),
Vector3.new(block.Position.X, 0, block.Position.Z - 3.5),
Vector3.new(block.Position.X, 3.5, block.Position.Z),
Vector3.new(block.Position.X, -3.5, block.Position.Z),
}
local guis = {
"RightGui", "LeftGui", "BackGui", "FrontGui", "TopGui", "BottomGui"
}
for i, pos in ipairs(neighbors) do
local neighborBlock = findBlockAtPosition(pos)
if neighborBlock then
local myGui = block:FindFirstChild(guis[i])
local neighborGui = neighborBlock:FindFirstChild(guis[i == 1 and 2 or (i == 2 and 1 or (i == 3 and 4 or (i == 4 and 3 or (i == 5 and 6 or 5))))])
if myGui then
myGui.Enabled = false
end
if neighborGui then
neighborGui.Enabled = false
end
end
end
end
local function updateNeighborsGUIs(block)
local neighbors = {
Vector3.new(block.Position.X + 3.5, 0, block.Position.Z),
Vector3.new(block.Position.X - 3.5, 0, block.Position.Z),
Vector3.new(block.Position.X, 0, block.Position.Z + 3.5),
Vector3.new(block.Position.X, 0, block.Position.Z - 3.5),
Vector3.new(block.Position.X, 3.5, block.Position.Z),
Vector3.new(block.Position.X, -3.5, block.Position.Z),
}
local guis = {
"LeftGui", "RightGui", "FrontGui", "BackGui", "BottomGui", "TopGui"
}
for i, pos in ipairs(neighbors) do
local neighborBlock = findBlockAtPosition(pos)
if neighborBlock then
local neighborGui = neighborBlock:FindFirstChild(guis[i])
if neighborGui then
neighborGui.Enabled = true
end
end
end
end
local function generateChunk(cx, cz)
if activeChunks[cx] and activeChunks[cx][cz] then
return
end
local newChunk = Instance.new("Folder")
newChunk.Name = string.format("Chunk_%d_%d", cx, cz)
newChunk.Parent = WorkspaceBlocks
for x = 0, CHUNK_SIZE - 1 do
for z = 0, CHUNK_SIZE - 1 do
local clone = GrassBlock:Clone()
clone.Parent = newChunk
clone.Position = Vector3.new((cx * CHUNK_SIZE + x) * 3.5, 0, (cz * CHUNK_SIZE + z) * 3.5)
clone.Orientation = Vector3.new(0, math.random(0,3)*90, 0)
clone.CanCollide = true
clone.CanTouch = false
clone.CanQuery = false
end
end
if not activeChunks[cx] then
activeChunks[cx] = {}
end
activeChunks[cx][cz] = newChunk
for _, block in ipairs(newChunk:GetChildren()) do
updateBlockGUIs(block)
end
end
local function despawnChunk(cx, cz)
if activeChunks[cx] and activeChunks[cx][cz] then
activeChunks[cx][cz]:Destroy()
activeChunks[cx][cz] = nil
end
end
RunService.Stepped:Connect(function()
local playersInChunks = {}
for _, player in ipairs(Players:GetPlayers()) do
if player and player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
local playerPos = player.Character.HumanoidRootPart.Position
local currentChunkX = math.floor(playerPos.X / (CHUNK_SIZE * 3.5))
local currentChunkZ = math.floor(playerPos.Z / (CHUNK_SIZE * 3.5))
playersInChunks[string.format("%d,%d", currentChunkX, currentChunkZ)] = true
for x = -RENDER_DISTANCE, RENDER_DISTANCE do
for z = -RENDER_DISTANCE, RENDER_DISTANCE do
generateChunk(currentChunkX + x, currentChunkZ + z)
end
end
end
end
for cx, zTable in pairs(activeChunks) do
for cz, chunkFolder in pairs(zTable) do
local isNearAnyPlayer = false
for _, player in ipairs(Players:GetPlayers()) do
if player and player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
local playerPos = player.Character.HumanoidRootPart.Position
local currentChunkX = math.floor(playerPos.X / (CHUNK_SIZE * 3.5))
local currentChunkZ = math.floor(playerPos.Z / (CHUNK_SIZE * 3.5))
local distanceX = math.abs(cx - currentChunkX)
local distanceZ = math.abs(cz - currentChunkZ)
if distanceX <= RENDER_DISTANCE and distanceZ <= RENDER_DISTANCE then
isNearAnyPlayer = true
break
end
end
end
if not isNearAnyPlayer then
despawnChunk(cx, cz)
end
end
end
end)
WorkspaceBlocks.DescendantAdded:Connect(function(child)
if child:IsA("BasePart") then
updateBlockGUIs(child)
end
end)
WorkspaceBlocks.DescendantRemoving:Connect(function(child)
if child:IsA("BasePart") then
updateNeighborsGUIs(child)
end
end)