280 lines
6.9 KiB
Lua
280 lines
6.9 KiB
Lua
--!native
|
|
--!optimize 2
|
|
|
|
local ChunkBuilder = {}
|
|
|
|
local Chunk = require("./Chunk")
|
|
local BlockManager = require("./BlockManager")
|
|
local util = require("../Util")
|
|
|
|
local objects = script.Parent.Parent.Parent.Objects
|
|
|
|
local RunService = game:GetService("RunService")
|
|
|
|
local ChunkBorderFolder = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
|
|
local DEBUG_GHOST = true
|
|
|
|
local NEIGHBOR_OFFSETS = {
|
|
{-1, 0, 0}, {1, 0, 0},
|
|
{0, -1, 0}, {0, 1, 0},
|
|
{0, 0, -1}, {0, 0, 1}
|
|
}
|
|
|
|
-- TODO: Move to Chunk
|
|
type BlockData = {
|
|
id: number,
|
|
state: {
|
|
[typeof("")]: string | boolean | number
|
|
}
|
|
}
|
|
|
|
local function placeBorder(a,b,c)
|
|
local pos = util.ChunkPosToCFrame(Vector3.new(a,b,c),Vector3.new(1,1,1)).Position - Vector3.new(2,2,2)
|
|
local d = objects.ChunkLoading:Clone()
|
|
d:PivotTo(CFrame.new(pos))
|
|
d.Parent = ChunkBorderFolder
|
|
return d
|
|
end
|
|
|
|
local function Swait(l)
|
|
for i = 1,l do
|
|
RunService.Stepped:Wait()
|
|
end
|
|
end
|
|
|
|
local function propogateNeighboringBlockChanges(cx, cy, cz, x, y, z)
|
|
--warn("propogateNeighboringBlockChanges",cx,cy,cz,x,y,z)
|
|
-- updates block in another chunk
|
|
local c = Chunk.AllChunks[`{cx},{cy},{cz}`]
|
|
if not c then return end
|
|
|
|
local d = c.data[`{x},{y},{z}`]
|
|
if not d then return end
|
|
|
|
if c:IsBlockRenderable(x, y, z) then
|
|
if c.instance:FindFirstChild(`{x},{y},{z}`) then return end
|
|
task.synchronize()
|
|
local block = BlockManager:GetBlockRotated(d.id, util.RotationStringToNormalId(d.state["r"] or "f"), d.state)
|
|
block.Name = `{x},{y},{z}`
|
|
block:PivotTo(util.ChunkPosToCFrame(c.pos, Vector3.new(x, y, z)))
|
|
block.Parent = c.instance
|
|
task.desynchronize()
|
|
else
|
|
local existing = c.instance:FindFirstChild(`{x},{y},{z}`)
|
|
if existing then
|
|
task.synchronize()
|
|
existing:Destroy()
|
|
task.desynchronize()
|
|
end
|
|
end
|
|
end
|
|
|
|
function ChunkBuilder:BuildChunk(c: typeof(Chunk.new(0,0,0)),parent: Instance?)
|
|
|
|
if c.loaded then
|
|
return c.instance
|
|
end
|
|
|
|
local blocks = c.data
|
|
local newcache = {} :: {[typeof("")]: BlockData}
|
|
|
|
local finished = false
|
|
|
|
|
|
local ch = Instance.new("Folder")
|
|
ch.Parent = parent
|
|
ch.Name = `{c.pos.X},{c.pos.Y},{c.pos.Z}`
|
|
|
|
local conn = c.UpdateBlockBindableL.Event:Connect(function(x: number, y: number, z: number, d: BlockData)
|
|
task.desynchronize()
|
|
if finished == false then
|
|
newcache[`{x},{y},{z}`] = d
|
|
return
|
|
end
|
|
task.synchronize()
|
|
for _, o in pairs(NEIGHBOR_OFFSETS) do
|
|
--warn("propogate",o[1],o[2],o[3])
|
|
-- Adjust for chunk boundaries
|
|
local b = {x = x + o[1], y = y + o[2], z = z + o[3]}
|
|
local ch = {x = c.pos.X, y = c.pos.Y, z = c.pos.Z}
|
|
|
|
if b.x < 1 then ch.x = c.pos.X - 1 b.x = 8 end
|
|
if b.x > 8 then ch.x = c.pos.X + 1 b.x = 1 end
|
|
if b.y < 1 then ch.y = c.pos.Y - 1 b.y = 8 end
|
|
if b.y > 8 then ch.y = c.pos.Y + 1 b.y = 1 end
|
|
if b.z < 1 then ch.z = c.pos.Z - 1 b.z = 8 end
|
|
if b.z > 8 then ch.z = c.pos.Z + 1 b.z = 1 end
|
|
|
|
propogateNeighboringBlockChanges(ch.x, ch.y, ch.z, b.x, b.y, b.z)
|
|
--BlockManager:GetBlock(ch.x)
|
|
end
|
|
|
|
local blockName = `{x},{y},{z}`
|
|
local existing = ch:FindFirstChild(blockName)
|
|
if d == 0 then
|
|
if c.delayedRemoval and c.delayedRemoval[blockName] then
|
|
c.delayedRemoval[blockName] = nil
|
|
if existing then
|
|
task.defer(function()
|
|
task.synchronize()
|
|
RunService.RenderStepped:Wait()
|
|
if existing.Parent then
|
|
existing:Destroy()
|
|
end
|
|
task.desynchronize()
|
|
end)
|
|
elseif DEBUG_GHOST then
|
|
print("[CHUNKBUILDER][GHOST] Delayed remove missing instance", c.pos, blockName)
|
|
end
|
|
return
|
|
end
|
|
if existing then
|
|
task.synchronize()
|
|
existing:Destroy()
|
|
task.desynchronize()
|
|
elseif DEBUG_GHOST then
|
|
print("[CHUNKBUILDER][GHOST] Remove missing instance", c.pos, blockName)
|
|
end
|
|
return
|
|
end
|
|
if not c:IsBlockRenderable(x, y, z) then
|
|
if existing then
|
|
task.synchronize()
|
|
existing:Destroy()
|
|
task.desynchronize()
|
|
end
|
|
return
|
|
end
|
|
if existing then
|
|
task.synchronize()
|
|
existing:Destroy()
|
|
task.desynchronize()
|
|
end
|
|
if not d then return end
|
|
if d.id == 0 then return end
|
|
local N = util.RotationStringToNormalId(d.state["r"] or "f")
|
|
task.synchronize()
|
|
local block = BlockManager:GetBlockRotated(d.id, N, d.state)
|
|
block.Name = blockName
|
|
block:PivotTo(util.ChunkPosToCFrame(c.pos, Vector3.new(x, y, z)))
|
|
block.Parent = ch
|
|
task.desynchronize()
|
|
end)
|
|
|
|
c.unloadChunkHook = function()
|
|
conn:Disconnect()
|
|
blocks = nil
|
|
c = nil
|
|
end
|
|
|
|
task.defer(function()
|
|
|
|
local p = 0
|
|
|
|
task.synchronize()
|
|
|
|
local hb = false
|
|
|
|
for a,b in pairs(blocks) do
|
|
hb = true
|
|
end
|
|
|
|
local border = Instance.new("Part")
|
|
if hb == true then
|
|
border:Destroy()
|
|
border = placeBorder(c.pos.X, c.pos.Y, c.pos.Z)
|
|
end
|
|
|
|
for a,b in pairs(blocks) do
|
|
task.desynchronize()
|
|
local coords = util.BlockPosStringToCoords(a)
|
|
if not c:IsBlockRenderable(coords.X, coords.Y, coords.Z) then
|
|
if ch:FindFirstChild(a) then
|
|
task.synchronize()
|
|
ch:FindFirstChild(a):Destroy()
|
|
task.desynchronize()
|
|
end
|
|
continue
|
|
end
|
|
task.desynchronize()
|
|
local N = util.RotationStringToNormalId(b.state["r"] or "f")
|
|
task.synchronize()
|
|
local block = BlockManager:GetBlockRotated(b.id, N, b.state)
|
|
if ch:FindFirstChild(a) then
|
|
ch:FindFirstChild(a):Destroy()
|
|
end
|
|
block.Name = a
|
|
block:PivotTo(util.ChunkPosToCFrame(c.pos, coords))
|
|
block.Parent = ch
|
|
p += 1
|
|
if p == 15 then
|
|
p = 0
|
|
Swait(1)
|
|
end
|
|
end
|
|
|
|
finished = true
|
|
|
|
task.synchronize()
|
|
border:Destroy()
|
|
task.desynchronize()
|
|
|
|
task.defer(function()
|
|
task.synchronize()
|
|
for key, data in pairs(newcache) do
|
|
local coords = util.BlockPosStringToCoords(key)
|
|
for _, o in pairs(NEIGHBOR_OFFSETS) do
|
|
-- chunks are 8x8x8
|
|
local nb = {x = coords.X + o[1], y = coords.Y + o[2], z = coords.Z + o[3]}
|
|
local chCoords = {x = c.pos.X, y = c.pos.Y, z = c.pos.Z}
|
|
if nb.x == 0 then chCoords.x = c.pos.X - 1 nb.x = 8 end
|
|
if nb.x == 9 then chCoords.x = c.pos.X + 1 nb.x = 1 end
|
|
|
|
if nb.y == 0 then chCoords.y = c.pos.Y - 1 nb.y = 8 end
|
|
if nb.y == 9 then chCoords.y = c.pos.Y + 1 nb.y = 1 end
|
|
|
|
if nb.z == 0 then chCoords.z = c.pos.Z - 1 nb.z = 8 end
|
|
if nb.z == 9 then chCoords.z = c.pos.Z + 1 nb.z = 1 end
|
|
|
|
propogateNeighboringBlockChanges(chCoords.x, chCoords.y, chCoords.z, nb.x, nb.y, nb.z)
|
|
end
|
|
|
|
local existing = ch:FindFirstChild(key)
|
|
if data == 0 or (data and data.id == 0) then
|
|
if existing then
|
|
existing:Destroy()
|
|
end
|
|
continue
|
|
end
|
|
if not c:IsBlockRenderable(coords.X, coords.Y, coords.Z) then
|
|
if existing then
|
|
existing:Destroy()
|
|
end
|
|
continue
|
|
end
|
|
if existing then
|
|
existing:Destroy()
|
|
end
|
|
if not data then
|
|
continue
|
|
end
|
|
local N = util.RotationStringToNormalId(data.state["r"] or "f")
|
|
local block = BlockManager:GetBlockRotated(data.id, N, data.state)
|
|
block.Name = key
|
|
block:PivotTo(util.ChunkPosToCFrame(c.pos, coords))
|
|
block.Parent = ch
|
|
end
|
|
newcache = nil
|
|
blocks = nil
|
|
end)
|
|
task.desynchronize()
|
|
end)
|
|
|
|
c.loaded = true
|
|
|
|
return ch
|
|
|
|
end
|
|
|
|
return ChunkBuilder
|