core: improved building system

This commit is contained in:
2026-01-06 23:44:07 +02:00
parent 0ac6d6e214
commit 97e142d3b2
6 changed files with 286 additions and 18 deletions

View File

@@ -5,6 +5,8 @@ local RunService = game:GetService("RunService")
local Chunk = require("./ChunkManager/Chunk")
local BlockManager = require("./ChunkManager/BlockManager")
local ChunkBuilder = require("./ChunkManager/ChunkBuilder")
local Util = require("./Util")
local Globals = require(script.Parent:WaitForChild("Globals"))
local remote = game:GetService("ReplicatedStorage"):WaitForChild("RecieveChunkPacket")
local tickremote = game:GetService("ReplicatedStorage"):WaitForChild("Tick")
@@ -14,8 +16,11 @@ ChunkFolder.Name = "$blockscraft_client"
ChunkManager.ChunkFolder = ChunkFolder
local CHUNK_RADIUS = 5
local LOAD_BATCH = 8
local CHUNK_RADIUS = Globals.RenderDistance or 5
local LOAD_BATCH = Globals.LoadBatch or 8
local RESYNC_INTERVAL = Globals.ResyncInterval or 5
local RESYNC_RADIUS = Globals.ResyncRadius or 2
local DEBUG_RESYNC = true
local FORCELOAD_CHUNKS = {
{0, 1, 0}
}
@@ -111,6 +116,100 @@ function ChunkManager:LoadChunk(x, y, z)
end)
end
function ChunkManager:RefreshChunk(x, y, z)
local key = `{x},{y},{z}`
local chunk = Chunk.AllChunks[key]
if not chunk then
pendingChunkRequests[key] = nil
ChunkManager:GetChunk(x, y, z)
ChunkManager:LoadChunk(x, y, z)
return
end
task.synchronize()
local ok, newData = pcall(function()
return remote:InvokeServer(x, y, z)
end)
if not ok or typeof(newData) ~= "table" then
if DEBUG_RESYNC then
warn("[CHUNKMANAGER][RESYNC] Failed to fetch chunk data", key, ok, typeof(newData))
end
return
end
local function sameState(a, b)
if a == b then
return true
end
if not a or not b then
return false
end
local count = 0
for k, v in pairs(a) do
count += 1
if b[k] ~= v then
return false
end
end
for _ in pairs(b) do
count -= 1
end
return count == 0
end
local function sameBlockData(a, b)
if a == b then
return true
end
if not a or not b then
return false
end
if a.id ~= b.id then
return false
end
return sameState(a.state, b.state)
end
local changed = 0
local removed = 0
for keyStr, newBlock in pairs(newData) do
local oldBlock = chunk.data[keyStr]
if not sameBlockData(oldBlock, newBlock) then
chunk.data[keyStr] = newBlock
local coords = Util.BlockPosStringToCoords(keyStr)
chunk:PropogateChanges(coords.X, coords.Y, coords.Z, newBlock)
changed += 1
end
end
for keyStr in pairs(chunk.data) do
if not newData[keyStr] then
chunk.data[keyStr] = nil
local coords = Util.BlockPosStringToCoords(keyStr)
chunk:PropogateChanges(coords.X, coords.Y, coords.Z, 0)
removed += 1
end
end
if chunk.loaded and chunk.instance then
local pruned = 0
for _, child in ipairs(chunk.instance:GetChildren()) do
if not newData[child.Name] then
pruned += 1
task.synchronize()
child:Destroy()
task.desynchronize()
end
end
if DEBUG_RESYNC and pruned > 0 then
print("[CHUNKMANAGER][RESYNC] Pruned ghost instances", key, "count", pruned)
end
end
if DEBUG_RESYNC and (changed > 0 or removed > 0) then
print("[CHUNKMANAGER][RESYNC] Applied diff", key, "changed", changed, "removed", removed)
end
task.desynchronize()
end
function ChunkManager:ForceTick()
for _, coords in ipairs(FORCELOAD_CHUNKS) do
local key = `{coords[1]},{coords[2]},{coords[3]}`
@@ -199,6 +298,37 @@ function ChunkManager:Tick()
end
end
function ChunkManager:ResyncAroundPlayer(radius: number)
local player = game:GetService("Players").LocalPlayer
if not player.Character then
return
end
local pos = player.Character:GetPivot().Position
local chunkPos = {
x = math.round(pos.X / 32),
y = math.round(pos.Y / 32),
z = math.round(pos.Z / 32)
}
for y = -radius, radius do
for x = -radius, radius do
for z = -radius, radius do
local cx, cy, cz = chunkPos.x + x, chunkPos.y + y, chunkPos.z + z
ChunkManager:RefreshChunk(cx, cy, cz)
end
end
end
end
function ChunkManager:ResyncAroundChunk(cx: number, cy: number, cz: number, radius: number)
for y = -radius, radius do
for x = -radius, radius do
for z = -radius, radius do
ChunkManager:RefreshChunk(cx + x, cy + y, cz + z)
end
end
end
end
function ChunkManager:Init()
if not RunService:IsClient() then
error("ChunkManager:Init can only be called on the client")
@@ -227,6 +357,18 @@ function ChunkManager:Init()
Swait(20)
end
end)
task.defer(function()
while true do
wait(RESYNC_INTERVAL)
local ok, err = pcall(function()
ChunkManager:ResyncAroundPlayer(RESYNC_RADIUS)
end)
if not ok then
warn("[CHUNKMANAGER][RESYNC]", err)
end
end
end)
end
return ChunkManager