Minecraft-like Roblox block game rblx.games/135624152691584
roblox roblox-game rojo
at main 274 lines 8.0 kB view raw
1--!native 2--!optimize 2 3 4local ReplicatedStorage = game:GetService("ReplicatedStorage") 5local ServerStorage = game:GetService("ServerStorage") 6local ClientStateService = require(script.Parent.ClientState) 7 8local Shared = ReplicatedStorage:WaitForChild("Shared") 9local ModsFolder = ReplicatedStorage:WaitForChild("Mods") 10local BlocksFolderRS = ReplicatedStorage:FindFirstChild("Blocks") or Instance.new("Folder") 11BlocksFolderRS.Name = "Blocks" 12BlocksFolderRS.Parent = ReplicatedStorage 13local BlocksFolderSS = ServerStorage:FindFirstChild("Blocks") or Instance.new("Folder") 14BlocksFolderSS.Name = "Blocks" 15BlocksFolderSS.Parent = ServerStorage 16 17local Util = require(Shared.Util) 18local TG = require(script.TerrainGen) 19local Players = game:GetService("Players") 20 21local blockIdMap = {} 22local rebuildBlockIdMap 23 24local function syncBlocksToServerStorage() 25 BlocksFolderSS:ClearAllChildren() 26 for _, child in ipairs(BlocksFolderRS:GetChildren()) do 27 child:Clone().Parent = BlocksFolderSS 28 end 29 ClientStateService:SetBlocksFolder(BlocksFolderSS) 30 if rebuildBlockIdMap then 31 rebuildBlockIdMap() 32 end 33end 34 35BlocksFolderRS.ChildAdded:Connect(syncBlocksToServerStorage) 36BlocksFolderRS.ChildRemoved:Connect(syncBlocksToServerStorage) 37 38do 39 local workspaceModFolder = game:GetService("Workspace"):WaitForChild("mods") 40 41 for _,v in pairs(workspaceModFolder:GetChildren()) do 42 v.Parent = ModsFolder 43 end 44 workspaceModFolder:Destroy() 45end 46 47local ML = require(Shared.ModLoader) 48ML.loadModsS() 49syncBlocksToServerStorage() 50ClientStateService:Init() 51 52do 53 local bv = Instance.new("BoolValue") 54 bv.Name = "MLLoaded" 55 bv.Value = true 56 bv.Parent = ReplicatedStorage:WaitForChild("Objects") 57end 58 59local MAX_CHUNK_DIST = 200 60 61local FakeChunk = TG:GetFakeChunk(-5,-5,-5) 62 63task.synchronize() 64 65ReplicatedStorage.Tick.OnServerEvent:Connect(function(player: Player, v: string) 66 if TG.ServerChunkCache[v] then 67 pcall(function() 68 TG.ServerChunkCache[v].inhabitedTime = tick() 69 end) 70 end 71end) 72 73ReplicatedStorage.RecieveChunkPacket.OnServerInvoke = function(plr: Player, x: number, y: number, z: number) 74 -- validate xyz type and limit 75 if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then 76 return {} 77 end 78 79 if math.abs(x) > MAX_CHUNK_DIST or math.abs(y) > MAX_CHUNK_DIST or math.abs(z) > MAX_CHUNK_DIST then 80 return FakeChunk.data 81 end 82 83 task.desynchronize() 84 local chunk = TG:GetChunk(x, y, z) 85 local chunkdata = chunk.data 86 task.synchronize() 87 88 return chunkdata 89 90end 91 92local tickRemote = ReplicatedStorage.Tick 93local remotes = ReplicatedStorage:WaitForChild("Remotes") 94local placeRemote = remotes:WaitForChild("PlaceBlock") 95local breakRemote = remotes:WaitForChild("BreakBlock") 96local blocksFolder = BlocksFolderSS 97local function propogate(a, cx, cy, cz, x, y, z, bd) 98 task.synchronize() 99 tickRemote:FireAllClients(a, cx, cy, cz, x, y, z, bd) 100 task.desynchronize() 101end 102 103local MAX_REACH = 512 104 105rebuildBlockIdMap = function() 106 table.clear(blockIdMap) 107 for _, block in ipairs(blocksFolder:GetChildren()) do 108 local id = block:GetAttribute("n") 109 if id ~= nil then 110 blockIdMap[id] = id 111 blockIdMap[tostring(id)] = id 112 end 113 end 114end 115 116rebuildBlockIdMap() 117blocksFolder.ChildAdded:Connect(rebuildBlockIdMap) 118blocksFolder.ChildRemoved:Connect(rebuildBlockIdMap) 119 120local function getPlayerPosition(player: Player): Vector3? 121 local character = player.Character 122 if not character then 123 return nil 124 end 125 local root = character:FindFirstChild("HumanoidRootPart") 126 if not root then 127 return nil 128 end 129 return root.Position 130end 131 132local function isWithinReach(player: Player, cx: number, cy: number, cz: number, x: number, y: number, z: number): boolean 133 -- Relaxed reach; always true unless you want to re-enable limits 134 return true 135end 136 137local function resolveBlockId(blockId: any): string | number | nil 138 return blockIdMap[blockId] 139end 140 141local function playerCanUseBlock(player: Player, resolvedId: any): boolean 142 if not ClientStateService:HasInInventory(player, resolvedId) then 143 return false 144 end 145 local selected = ClientStateService:GetSelectedBlockId(player) 146 if not selected then 147 return false 148 end 149 return tostring(selected) == tostring(resolvedId) 150end 151 152local function getServerChunk(cx: number, cy: number, cz: number) 153 task.desynchronize() 154 local chunk = TG:GetChunk(cx, cy, cz) 155 task.synchronize() 156 return chunk 157end 158 159-- local PLAYER_BOX_SIZE = Vector3.new(3, 6, 3) 160 161local function isBlockInsidePlayer(blockPos: Vector3): boolean 162 for _, player in ipairs(Players:GetPlayers()) do 163 local character = player.Character 164 if character then 165 local cf, size = character:GetBoundingBox() 166 local localPos = cf:PointToObjectSpace(blockPos) 167 if math.abs(localPos.X) <= size.X * 0.5 168 and math.abs(localPos.Y) <= size.Y * 0.5 169 and math.abs(localPos.Z) <= size.Z * 0.5 then 170 return true 171 end 172 end 173 end 174 return false 175end 176 177local DEBUG_PLACEMENT = false 178local function debugPlacementLog(...: any) 179 if DEBUG_PLACEMENT then 180 Util.StudioLog(...) 181 end 182end 183 184local function debugPlacementWarn(...: any) 185 if DEBUG_PLACEMENT then 186 Util.StudioWarn(...) 187 end 188end 189 190placeRemote.OnServerEvent:Connect(function(player, cx, cy, cz, x, y, z, blockId) 191 local function reject(reason: string) 192 debugPlacementWarn("[PLACE][REJECT]", player.Name, reason, "chunk", cx, cy, cz, "block", x, y, z, "id", blockId) 193 return 194 end 195 196 if typeof(cx) ~= "number" or typeof(cy) ~= "number" or typeof(cz) ~= "number" then 197 return reject("chunk types") 198 end 199 if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then 200 return reject("block types") 201 end 202 if x < 1 or x > 8 or y < 1 or y > 8 or z < 1 or z > 8 then 203 return reject("block bounds") 204 end 205 if math.abs(cx) > MAX_CHUNK_DIST or math.abs(cy) > MAX_CHUNK_DIST or math.abs(cz) > MAX_CHUNK_DIST then 206 return reject("chunk bounds") 207 end 208 if not isWithinReach(player, cx, cy, cz, x, y, z) then 209 return reject("out of reach") 210 end 211 local resolvedId = resolveBlockId(blockId) 212 if not resolvedId then 213 return reject("invalid id") 214 end 215 if not playerCanUseBlock(player, resolvedId) then 216 return reject("not in inventory/hotbar") 217 end 218 219 local blockPos = Util.ChunkPosToCFrame(Vector3.new(cx, cy, cz), Vector3.new(x, y, z)).Position 220 if isBlockInsidePlayer(blockPos) then 221 return reject("inside player") 222 end 223 224 local chunk = getServerChunk(cx, cy, cz) 225 local existing = chunk:GetBlockAt(x, y, z) 226 if existing and existing.id and existing.id ~= 0 then 227 if existing.id == resolvedId then 228 -- same block already there; treat as success without changes 229 debugPlacementLog("[PLACE][OK][NOOP]", player.Name, "chunk", cx, cy, cz, "block", x, y, z, "id", resolvedId) 230 return 231 end 232 -- allow replacement when different id: remove then place 233 chunk:RemoveBlock(x, y, z) 234 end 235 local data = { 236 id = resolvedId, 237 state = {} 238 } 239 chunk:CreateBlock(x, y, z, data) 240 propogate("B_C", cx, cy, cz, x, y, z, data) 241 debugPlacementLog("[PLACE][OK]", player.Name, "chunk", cx, cy, cz, "block", x, y, z, "id", resolvedId) 242end) 243 244breakRemote.OnServerEvent:Connect(function(player, cx, cy, cz, x, y, z) 245 if typeof(cx) ~= "number" or typeof(cy) ~= "number" or typeof(cz) ~= "number" then 246 return 247 end 248 if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then 249 return 250 end 251 if x < 1 or x > 8 or y < 1 or y > 8 or z < 1 or z > 8 then 252 return 253 end 254 if math.abs(cx) > MAX_CHUNK_DIST or math.abs(cy) > MAX_CHUNK_DIST or math.abs(cz) > MAX_CHUNK_DIST then 255 return 256 end 257 if not isWithinReach(player, cx, cy, cz, x, y, z) then 258 return 259 end 260 261 local chunk = getServerChunk(cx, cy, cz) 262 if not chunk:GetBlockAt(x, y, z) then 263 task.synchronize() 264 tickRemote:FireClient(player, "C_R", cx, cy, cz, 0, 0, 0, 0) 265 task.desynchronize() 266 debugPlacementLog("[BREAK][NOOP]", player.Name, "chunk", cx, cy, cz, "block", x, y, z) 267 return 268 end 269 chunk:RemoveBlock(x, y, z) 270 propogate("B_D", cx, cy, cz, x, y, z, 0) 271 debugPlacementLog("[BREAK][OK]", player.Name, "chunk", cx, cy, cz, "block", x, y, z) 272end) 273 274task.desynchronize()