···158158end
159159160160function Chunk:RemoveBlock(x, y, z)
161161- print("[DEBUG] Chunk:RemoveBlock called - Chunk:", self.pos, "Block coords:", x, y, z)
162161 local blockKey = keyFromCoords(x, y, z)
163163- local existingBlock = self.data[blockKey]
164164- if existingBlock then
165165- print("[DEBUG] Removing existing block with ID:", existingBlock.id)
166166- else
167167- print("[DEBUG] No block found at coords", x, y, z)
168168- end
169162 self.data[blockKey] = nil
170163 self:PropogateChanges(x,y,z,0)
171164end
172165173166function Chunk:RemoveBlockSmooth(x, y, z)
174174- print("[DEBUG] Chunk:RemoveBlockSmooth called - Chunk:", self.pos, "Block coords:", x, y, z)
175167 local blockKey = keyFromCoords(x, y, z)
176176- local existingBlock = self.data[blockKey]
177177- if existingBlock then
178178- print("[DEBUG] Smooth removing existing block with ID:", existingBlock.id)
179179- else
180180- print("[DEBUG] Smooth remove: no block found at coords", x, y, z)
181181- end
182168 self.data[blockKey] = nil
183169 self.delayedRemoval[blockKey] = true
184170 self:PropogateChanges(x,y,z,0)
+105-40
src/ReplicatedStorage/Shared/PlacementManager.lua
···5566local ChunkManager = require("./ChunkManager")
77local Util = require("./Util")
88-local RunService = game:GetService("RunService")
98109PlacementManager.ChunkFolder = ChunkManager.ChunkFolder
1110···33323433local Mouse: Mouse = nil
3534local lastNormalId: Enum.NormalId? = nil
3636-local pendingBreakResync = {}
3535+local BREAK_ROLLBACK_TIMEOUT = 0.6
3636+local pendingBreaks = {}
3737+3838+local function makeChunkKey(cx: number, cy: number, cz: number): string
3939+ return `{cx},{cy},{cz}`
4040+end
4141+4242+local function makeBlockKey(x: number, y: number, z: number): string
4343+ return `{x},{y},{z}`
4444+end
4545+4646+local function getPendingBreak(chunkKey: string, blockKey: string)
4747+ local chunkMap = pendingBreaks[chunkKey]
4848+ if not chunkMap then
4949+ return nil
5050+ end
5151+ return chunkMap[blockKey]
5252+end
5353+5454+local function clearPendingBreak(chunkKey: string, blockKey: string)
5555+ local chunkMap = pendingBreaks[chunkKey]
5656+ if not chunkMap then
5757+ return
5858+ end
5959+ chunkMap[blockKey] = nil
6060+ if not next(chunkMap) then
6161+ pendingBreaks[chunkKey] = nil
6262+ end
6363+end
6464+6565+local function clearPendingBreaksForChunk(chunkKey: string)
6666+ pendingBreaks[chunkKey] = nil
6767+end
6868+6969+local function scheduleBreakRollback(cx: number, cy: number, cz: number, x: number, y: number, z: number)
7070+ task.delay(BREAK_ROLLBACK_TIMEOUT, function()
7171+ local chunkKey = makeChunkKey(cx, cy, cz)
7272+ local blockKey = makeBlockKey(x, y, z)
7373+ local pending = getPendingBreak(chunkKey, blockKey)
7474+ if not pending then
7575+ return
7676+ end
7777+ clearPendingBreak(chunkKey, blockKey)
7878+ local chunk = ChunkManager:GetChunk(cx, cy, cz)
7979+ if pending.data and chunk then
8080+ chunk:CreateBlock(x, y, z, pending.data)
8181+ end
8282+ ChunkManager:RefreshChunk(cx, cy, cz)
8383+ end)
8484+end
37853886local function normalIdToOffset(normal: Enum.NormalId): Vector3
3987 if normal == Enum.NormalId.Top then
···83131 return Vector3.new(cx, cy, cz), Vector3.new(bx, by, bz)
84132end
85133134134+local function getPlayerPosition(): Vector3?
135135+ local player = game:GetService("Players").LocalPlayer
136136+ local character = player and player.Character
137137+ if not character then
138138+ return nil
139139+ end
140140+ local root = character:FindFirstChild("HumanoidRootPart")
141141+ return root and root.Position or nil
142142+end
143143+144144+local function isWithinReach(cx: number, cy: number, cz: number, x: number, y: number, z: number): boolean
145145+ local playerPos = getPlayerPosition()
146146+ if not playerPos then
147147+ return false
148148+ end
149149+ local blockPos = Util.ChunkPosToCFrame(Vector3.new(cx, cy, cz), Vector3.new(x, y, z)).Position
150150+ return (blockPos - playerPos).Magnitude <= 24
151151+end
152152+86153-- Gets the block and normalid of the block (and surface) the player is looking at
87154function PlacementManager:Raycast()
88155 if not Mouse then
···133200134201-- FIRES REMOTE
135202function PlacementManager:BreakBlock(cx, cy, cz, x, y, z)
136136- print("[DEBUG] PlacementManager:BreakBlock called - Chunk:", cx, cy, cz, "Block:", x, y, z)
203203+ if typeof(cx) ~= "number" or typeof(cy) ~= "number" or typeof(cz) ~= "number" then
204204+ return
205205+ end
206206+ if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then
207207+ return
208208+ end
209209+ if x < 1 or x > 8 or y < 1 or y > 8 or z < 1 or z > 8 then
210210+ return
211211+ end
212212+ if not isWithinReach(cx, cy, cz, x, y, z) then
213213+ return
214214+ end
215215+137216 local chunk = ChunkManager:GetChunk(cx, cy, cz)
138138- if chunk and not chunk:GetBlockAt(x, y, z) then
139139- print("[DEBUG] Client missing block; resyncing nearby chunks")
140140- ChunkManager:ResyncAroundChunk(cx, cy, cz, 1)
141141- task.defer(function()
142142- task.synchronize()
143143- RunService.RenderStepped:Wait()
144144- task.desynchronize()
145145- local refreshed = ChunkManager:GetChunk(cx, cy, cz)
146146- if refreshed and refreshed:GetBlockAt(x, y, z) then
147147- task.synchronize()
148148- breakRemote:FireServer(cx, cy, cz, x, y, z)
149149- task.desynchronize()
150150- print("[DEBUG] BreakBlock remote fired to server after resync")
151151- end
152152- end)
217217+ local blockData = chunk and chunk:GetBlockAt(x, y, z) or nil
218218+ local chunkKey = makeChunkKey(cx, cy, cz)
219219+ local blockKey = makeBlockKey(x, y, z)
220220+ if getPendingBreak(chunkKey, blockKey) then
153221 return
154222 end
223223+ pendingBreaks[chunkKey] = pendingBreaks[chunkKey] or {}
224224+ pendingBreaks[chunkKey][blockKey] = {
225225+ data = blockData,
226226+ time = tick(),
227227+ }
228228+ if blockData then
229229+ chunk:RemoveBlock(x, y, z)
230230+ end
231231+ scheduleBreakRollback(cx, cy, cz, x, y, z)
155232 task.synchronize()
156233 breakRemote:FireServer(cx, cy, cz, x, y, z)
157234 task.desynchronize()
158158- print("[DEBUG] BreakBlock remote fired to server")
159235end
160236161237-- CLIENTSIDED: only apply server-validated changes
···166242167243-- CLIENTSIDED: only apply server-validated changes
168244local function applyBreakBlockLocal(cx, cy, cz, x, y, z)
169169- print("[DEBUG] PlacementManager:BreakBlockLocal called - Chunk:", cx, cy, cz, "Block:", x, y, z)
170245 local chunk = ChunkManager:GetChunk(cx, cy, cz)
171171- if chunk then
172172- print("[DEBUG] Found chunk, calling RemoveBlock")
173173- if chunk.RemoveBlockSmooth then
174174- chunk:RemoveBlockSmooth(x, y, z)
175175- else
176176- chunk:RemoveBlock(x, y, z)
177177- end
178178- else
179179- print("[DEBUG] Chunk not found at coords:", cx, cy, cz)
246246+ if not chunk then
247247+ return
248248+ end
249249+ local chunkKey = makeChunkKey(cx, cy, cz)
250250+ local blockKey = makeBlockKey(x, y, z)
251251+ if getPendingBreak(chunkKey, blockKey) then
252252+ clearPendingBreak(chunkKey, blockKey)
253253+ return
180254 end
255255+ chunk:RemoveBlock(x, y, z)
181256end
182257183258function PlacementManager:GetBlockAtMouse(): nil | {chunk:Vector3, block: Vector3}
···250325 end
251326 if m == "B_D" then
252327 applyBreakBlockLocal(cx, cy, cz, x, y ,z)
253253- local key = `{cx},{cy},{cz}`
254254- if not pendingBreakResync[key] then
255255- pendingBreakResync[key] = true
256256- task.defer(function()
257257- task.synchronize()
258258- RunService.RenderStepped:Wait()
259259- task.desynchronize()
260260- pendingBreakResync[key] = nil
261261- ChunkManager:ResyncAroundChunk(cx, cy, cz, 1)
262262- end)
263263- end
264328 end
265329 if m == "C_R" then
330330+ clearPendingBreaksForChunk(makeChunkKey(cx, cy, cz))
266331 ChunkManager:RefreshChunk(cx, cy, cz)
267332 end
268333 end)
···11+--!native
22+--!optimize 2
33+44+local Players = game:GetService("Players")
55+66+local SCALE = 1.4
77+88+local function applyScale(character: Model)
99+ if character.ScaleTo then
1010+ pcall(function()
1111+ character:ScaleTo(SCALE)
1212+ end)
1313+ return
1414+ end
1515+1616+ local humanoid = character:FindFirstChildOfClass("Humanoid")
1717+ if not humanoid then
1818+ return
1919+ end
2020+2121+ if humanoid.RigType == Enum.HumanoidRigType.R15 then
2222+ for _, name in ipairs({"BodyHeightScale", "BodyWidthScale", "BodyDepthScale", "HeadScale"}) do
2323+ local scaleValue = humanoid:FindFirstChild(name)
2424+ if scaleValue then
2525+ scaleValue.Value = SCALE
2626+ end
2727+ end
2828+ end
2929+end
3030+3131+local function onCharacterAdded(character: Model)
3232+ character:WaitForChild("Humanoid", 5)
3333+ applyScale(character)
3434+end
3535+3636+local function onPlayerAdded(player: Player)
3737+ player.CharacterAdded:Connect(onCharacterAdded)
3838+ if player.Character then
3939+ onCharacterAdded(player.Character)
4040+ end
4141+end
4242+4343+for _, player in ipairs(Players:GetPlayers()) do
4444+ onPlayerAdded(player)
4545+end
4646+4747+Players.PlayerAdded:Connect(onPlayerAdded)
···11--!native
22--!optimize 2
3344-print("Hello world!")
55-64task.synchronize()
7586local ReplicatedStorage = game:GetService("ReplicatedStorage")
···13111412local Util = require(Shared.Util)
1513local TG = require("./ServerChunkManager/TerrainGen")
1414+local Players = game:GetService("Players")
16151716do
1817 local workspaceModFolder = game:GetService("Workspace"):WaitForChild("mods")
···127126 return chunk
128127end
129128129129+local function isBlockInsidePlayer(blockPos: Vector3): boolean
130130+ for _, player in ipairs(Players:GetPlayers()) do
131131+ local character = player.Character
132132+ if character then
133133+ local cf, size = character:GetBoundingBox()
134134+ local localPos = cf:PointToObjectSpace(blockPos)
135135+ if math.abs(localPos.X) <= size.X * 0.5
136136+ and math.abs(localPos.Y) <= size.Y * 0.5
137137+ and math.abs(localPos.Z) <= size.Z * 0.5 then
138138+ return true
139139+ end
140140+ end
141141+ end
142142+ return false
143143+end
144144+130145placeRemote.OnServerEvent:Connect(function(player, cx, cy, cz, x, y, z, blockId)
131146 --print("place",player, cx, cy, cz, x, y, z, blockData)
132147···150165 return
151166 end
152167168168+ local blockPos = Util.ChunkPosToCFrame(Vector3.new(cx, cy, cz), Vector3.new(x, y, z)).Position
169169+ if isBlockInsidePlayer(blockPos) then
170170+ return
171171+ end
172172+153173 local chunk = getServerChunk(cx, cy, cz)
154174 if chunk:GetBlockAt(x, y, z) then
155175 return
···163183end)
164184165185breakRemote.OnServerEvent:Connect(function(player, cx, cy, cz, x, y, z)
166166- print("[DEBUG] Server breakRemote received - Player:", player.Name, "Chunk:", cx, cy, cz, "Block:", x, y, z)
167167-168186 if typeof(cx) ~= "number" or typeof(cy) ~= "number" or typeof(cz) ~= "number" then
169169- print("[DEBUG] Invalid chunk coordinate types")
170187 return
171188 end
172189 if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then
173173- print("[DEBUG] Invalid block coordinate types")
174190 return
175191 end
176192 if x < 1 or x > 8 or y < 1 or y > 8 or z < 1 or z > 8 then
177177- print("[DEBUG] Block coordinates out of range:", x, y, z)
178193 return
179194 end
180195 if math.abs(cx) > MAX_CHUNK_DIST or math.abs(cy) > MAX_CHUNK_DIST or math.abs(cz) > MAX_CHUNK_DIST then
181181- print("[DEBUG] Chunk coordinates out of range:", cx, cy, cz)
182196 return
183197 end
184198 if not isWithinReach(player, cx, cy, cz, x, y, z) then
185185- print("[DEBUG] Block not within player reach")
186199 return
187200 end
188201189202 local chunk = getServerChunk(cx, cy, cz)
190203 if not chunk:GetBlockAt(x, y, z) then
191191- print("[DEBUG] No block found at specified location")
192204 task.synchronize()
193205 tickRemote:FireClient(player, "C_R", cx, cy, cz, 0, 0, 0, 0)
194206 task.desynchronize()
195207 return
196208 end
197197- print("[DEBUG] All validations passed, removing block")
198209 chunk:RemoveBlock(x, y, z)
199210 propogate("B_D", cx, cy, cz, x, y, z, 0)
200200- print("[DEBUG] Block removal propagated to clients")
201211end)
202212203213task.desynchronize()