···55local Chunk = require("./ChunkManager/Chunk")
66local BlockManager = require("./ChunkManager/BlockManager")
77local ChunkBuilder = require("./ChunkManager/ChunkBuilder")
88+local Util = require("./Util")
99+local Globals = require(script.Parent:WaitForChild("Globals"))
810911local remote = game:GetService("ReplicatedStorage"):WaitForChild("RecieveChunkPacket")
1012local tickremote = game:GetService("ReplicatedStorage"):WaitForChild("Tick")
···14161517ChunkManager.ChunkFolder = ChunkFolder
16181717-local CHUNK_RADIUS = 5
1818-local LOAD_BATCH = 8
1919+local CHUNK_RADIUS = Globals.RenderDistance or 5
2020+local LOAD_BATCH = Globals.LoadBatch or 8
2121+local RESYNC_INTERVAL = Globals.ResyncInterval or 5
2222+local RESYNC_RADIUS = Globals.ResyncRadius or 2
2323+local DEBUG_RESYNC = true
1924local FORCELOAD_CHUNKS = {
2025 {0, 1, 0}
2126}
···111116 end)
112117end
113118119119+function ChunkManager:RefreshChunk(x, y, z)
120120+ local key = `{x},{y},{z}`
121121+ local chunk = Chunk.AllChunks[key]
122122+ if not chunk then
123123+ pendingChunkRequests[key] = nil
124124+ ChunkManager:GetChunk(x, y, z)
125125+ ChunkManager:LoadChunk(x, y, z)
126126+ return
127127+ end
128128+129129+ task.synchronize()
130130+ local ok, newData = pcall(function()
131131+ return remote:InvokeServer(x, y, z)
132132+ end)
133133+ if not ok or typeof(newData) ~= "table" then
134134+ if DEBUG_RESYNC then
135135+ warn("[CHUNKMANAGER][RESYNC] Failed to fetch chunk data", key, ok, typeof(newData))
136136+ end
137137+ return
138138+ end
139139+140140+ local function sameState(a, b)
141141+ if a == b then
142142+ return true
143143+ end
144144+ if not a or not b then
145145+ return false
146146+ end
147147+ local count = 0
148148+ for k, v in pairs(a) do
149149+ count += 1
150150+ if b[k] ~= v then
151151+ return false
152152+ end
153153+ end
154154+ for _ in pairs(b) do
155155+ count -= 1
156156+ end
157157+ return count == 0
158158+ end
159159+160160+ local function sameBlockData(a, b)
161161+ if a == b then
162162+ return true
163163+ end
164164+ if not a or not b then
165165+ return false
166166+ end
167167+ if a.id ~= b.id then
168168+ return false
169169+ end
170170+ return sameState(a.state, b.state)
171171+ end
172172+173173+ local changed = 0
174174+ local removed = 0
175175+ for keyStr, newBlock in pairs(newData) do
176176+ local oldBlock = chunk.data[keyStr]
177177+ if not sameBlockData(oldBlock, newBlock) then
178178+ chunk.data[keyStr] = newBlock
179179+ local coords = Util.BlockPosStringToCoords(keyStr)
180180+ chunk:PropogateChanges(coords.X, coords.Y, coords.Z, newBlock)
181181+ changed += 1
182182+ end
183183+ end
184184+185185+ for keyStr in pairs(chunk.data) do
186186+ if not newData[keyStr] then
187187+ chunk.data[keyStr] = nil
188188+ local coords = Util.BlockPosStringToCoords(keyStr)
189189+ chunk:PropogateChanges(coords.X, coords.Y, coords.Z, 0)
190190+ removed += 1
191191+ end
192192+ end
193193+ if chunk.loaded and chunk.instance then
194194+ local pruned = 0
195195+ for _, child in ipairs(chunk.instance:GetChildren()) do
196196+ if not newData[child.Name] then
197197+ pruned += 1
198198+ task.synchronize()
199199+ child:Destroy()
200200+ task.desynchronize()
201201+ end
202202+ end
203203+ if DEBUG_RESYNC and pruned > 0 then
204204+ print("[CHUNKMANAGER][RESYNC] Pruned ghost instances", key, "count", pruned)
205205+ end
206206+ end
207207+ if DEBUG_RESYNC and (changed > 0 or removed > 0) then
208208+ print("[CHUNKMANAGER][RESYNC] Applied diff", key, "changed", changed, "removed", removed)
209209+ end
210210+ task.desynchronize()
211211+end
212212+114213function ChunkManager:ForceTick()
115214 for _, coords in ipairs(FORCELOAD_CHUNKS) do
116215 local key = `{coords[1]},{coords[2]},{coords[3]}`
···199298 end
200299end
201300301301+function ChunkManager:ResyncAroundPlayer(radius: number)
302302+ local player = game:GetService("Players").LocalPlayer
303303+ if not player.Character then
304304+ return
305305+ end
306306+ local pos = player.Character:GetPivot().Position
307307+ local chunkPos = {
308308+ x = math.round(pos.X / 32),
309309+ y = math.round(pos.Y / 32),
310310+ z = math.round(pos.Z / 32)
311311+ }
312312+ for y = -radius, radius do
313313+ for x = -radius, radius do
314314+ for z = -radius, radius do
315315+ local cx, cy, cz = chunkPos.x + x, chunkPos.y + y, chunkPos.z + z
316316+ ChunkManager:RefreshChunk(cx, cy, cz)
317317+ end
318318+ end
319319+ end
320320+end
321321+322322+function ChunkManager:ResyncAroundChunk(cx: number, cy: number, cz: number, radius: number)
323323+ for y = -radius, radius do
324324+ for x = -radius, radius do
325325+ for z = -radius, radius do
326326+ ChunkManager:RefreshChunk(cx + x, cy + y, cz + z)
327327+ end
328328+ end
329329+ end
330330+end
331331+202332function ChunkManager:Init()
203333 if not RunService:IsClient() then
204334 error("ChunkManager:Init can only be called on the client")
···225355 end
226356 end)
227357 Swait(20)
358358+ end
359359+ end)
360360+361361+ task.defer(function()
362362+ while true do
363363+ wait(RESYNC_INTERVAL)
364364+ local ok, err = pcall(function()
365365+ ChunkManager:ResyncAroundPlayer(RESYNC_RADIUS)
366366+ end)
367367+ if not ok then
368368+ warn("[CHUNKMANAGER][RESYNC]", err)
369369+ end
228370 end
229371 end)
230372end
···160160end)
161161162162breakRemote.OnServerEvent:Connect(function(player, cx, cy, cz, x, y, z)
163163- --print("del",player, cx, cy, cz, x, y, z)
163163+ print("[DEBUG] Server breakRemote received - Player:", player.Name, "Chunk:", cx, cy, cz, "Block:", x, y, z)
164164165165 if typeof(cx) ~= "number" or typeof(cy) ~= "number" or typeof(cz) ~= "number" then
166166+ print("[DEBUG] Invalid chunk coordinate types")
166167 return
167168 end
168169 if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then
170170+ print("[DEBUG] Invalid block coordinate types")
169171 return
170172 end
171173 if x < 1 or x > 8 or y < 1 or y > 8 or z < 1 or z > 8 then
174174+ print("[DEBUG] Block coordinates out of range:", x, y, z)
172175 return
173176 end
174177 if math.abs(cx) > MAX_CHUNK_DIST or math.abs(cy) > MAX_CHUNK_DIST or math.abs(cz) > MAX_CHUNK_DIST then
178178+ print("[DEBUG] Chunk coordinates out of range:", cx, cy, cz)
175179 return
176180 end
177181 if not isWithinReach(player, cx, cy, cz, x, y, z) then
182182+ print("[DEBUG] Block not within player reach")
178183 return
179184 end
180185181186 local chunk = getServerChunk(cx, cy, cz)
182187 if not chunk:GetBlockAt(x, y, z) then
188188+ print("[DEBUG] No block found at specified location")
189189+ task.synchronize()
190190+ tickRemote:FireClient(player, "C_R", cx, cy, cz, 0, 0, 0, 0)
191191+ task.desynchronize()
183192 return
184193 end
194194+ print("[DEBUG] All validations passed, removing block")
185195 chunk:RemoveBlock(x, y, z)
186196 propogate("B_D", cx, cy, cz, x, y, z, 0)
197197+ print("[DEBUG] Block removal propagated to clients")
187198end)
188199189200task.desynchronize()