chore: init from rojo-to-rbxlx

This commit is contained in:
2026-01-06 07:56:21 +02:00
commit 2583f46d7d
33 changed files with 3723 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
local BlockManager = {}
BlockManager.BlockIdMappings = {} :: {BasePart}
for i,v in pairs(game:GetService("ReplicatedStorage"):WaitForChild("Blocks"):GetChildren()) do
BlockManager.BlockIdMappings[v:GetAttribute("n")] = v
end
BlockManager.UpdateIdMappings = {} :: {BasePart}
for i,v in pairs(game:GetService("ReplicatedStorage"):WaitForChild("BlockUpdateOperations"):GetChildren()) do
local success, reason = pcall(function()
BlockManager.UpdateIdMappings[v:GetAttribute("n")] = require(v)
end)
if not success then
warn("[BLOCKMANAGER] Invalid update operation",v:GetAttribute("n"),":",reason)
end
end
local warnedBlockIds = {}
function BlockManager:GetBlock(blockId: number, attr: {[typeof("")]: any}?)
task.synchronize()
if not BlockManager.BlockIdMappings[blockId] then
if not warnedBlockIds[blockId] then
warnedBlockIds[blockId] = true
warn("[BLOCKMANAGER] Invalid block id",blockId)
end
return script.invalid:Clone()
end
local b = BlockManager.BlockIdMappings[blockId]:Clone()
b.Size = Vector3.new(3.95,3.95,3.95)
for i,v in pairs(attr or {}) do
b:SetAttribute(i,v)
end
if BlockManager.UpdateIdMappings[blockId] then
local success, reason = pcall(function()
BlockManager.UpdateIdMappings[blockId](b)
end)
if not success then
warn("[BLOCKMANAGER] Failed update operation",blockId,":",reason)
end
end
return b
end
-- ChatGPT Generated Func!!!!
function BlockManager:GetBlockRotated(blockId: number, face: Enum.NormalId, attr: {[typeof("")]: any}?)
-- Returns block with id blockId, rotated so the given face (NormalId) points north (+X).
local block = BlockManager:GetBlock(blockId, attr)
local rot = CFrame.new()
task.synchronize()
if face == Enum.NormalId.Front then
rot = CFrame.Angles(0, 0, 0) -- no rot
elseif face == Enum.NormalId.Back then
rot = CFrame.Angles(0, math.rad(180), 0)
elseif face == Enum.NormalId.Left then
rot = CFrame.Angles(0, math.rad(90), 0)
elseif face == Enum.NormalId.Right then
rot = CFrame.Angles(0, math.rad(-90), 0)
elseif face == Enum.NormalId.Top then
rot = CFrame.Angles(math.rad(-90), 0, 0) -- top +x
elseif face == Enum.NormalId.Bottom then
rot = CFrame.Angles(math.rad(90), 0, 0) -- bottom +x
end
-- ocbwoy3's fix
block:PivotTo(rot)
block.CFrame = CFrame.new(rot.Position)
return block
end
return BlockManager

View File

@@ -0,0 +1,3 @@
{
"ignoreUnknownInstances": true
}

View File

@@ -0,0 +1,180 @@
local Chunk = {}
Chunk.__index = Chunk
Chunk.UpdateBlockBindable = Instance.new("BindableEvent") :: BindableEvent
Chunk.AllChunks = {} :: {[typeof("")]: typeof(Chunk.new(0,0,0))}
local RunService = game:GetService("RunService")
local function Swait(l)
for i = 1,l do
RunService.Stepped:Wait()
end
end
export type BlockData = {
id: number,
state: {
[typeof("")]: string | boolean | number
}
}
function Chunk.new(x,y,z)
local self = setmetatable({}, Chunk)
self.pos = Vector3.new(x,y,z)
-- Tick ONLY in a 5 chunk distance of LP's char
self.inhabitedTime = tick()
self.instance = Instance.new("Folder")
self.unloadChunkHook = function() end
self.UpdateBlockBindableL = Instance.new("BindableEvent") :: BindableEvent
self.loaded = false
self.loading = false
self.data = {} :: {[typeof("")]: BlockData} -- "X,Y,Z": BlockData ("-1,-1,1": BlockData)
return self
end
function Chunk.from(x,y,z,data)
local self = setmetatable({}, Chunk)
self.pos = Vector3.new(x,y,z)
-- Tick ONLY in a 5 chunk distance of LP's char
self.inhabitedTime = tick()
self.instance = Instance.new("Folder")
self.unloadChunkHook = function() end
self.UpdateBlockBindableL = Instance.new("BindableEvent") :: BindableEvent
self.data = data :: {[typeof("")]: BlockData} -- "X,Y,Z": BlockData ("-1,-1,1": BlockData)
return self
end
function Chunk:DoesNeighboringBlockExist(rx, ry, rz, gx, gy, gz, offsetX, offsetY, offsetZ)
task.desynchronize()
-- Calculate the local position of the neighboring block
local neighborRX, neighborRY, neighborRZ = rx + offsetX, ry + offsetY, rz + offsetZ
local neighborGX, neighborGY, neighborGZ = gx, gy, gz
-- Adjust for chunk boundaries
if neighborRX < 1 then
neighborRX = 8
neighborGX = gx - 1
elseif neighborRX > 8 then
neighborRX = 1
neighborGX = gx + 1
end
if neighborRY < 1 then
neighborRY = 8
neighborGY = gy - 1
elseif neighborRY > 8 then
neighborRY = 1
neighborGY = gy + 1
end
if neighborRZ < 1 then
neighborRZ = 8
neighborGZ = gz - 1
elseif neighborRZ > 8 then
neighborRZ = 1
neighborGZ = gz + 1
end
if neighborGY < 0 then
return true
end
-- Get the neighboring chunk
local neighborChunk = Chunk.AllChunks[`{neighborGX},{neighborGY},{neighborGZ}`]
if not neighborChunk then
return true -- Assume block exists in an unloaded chunk
end
-- Check if the block exists in the neighboring chunk
return neighborChunk:GetBlockAt(neighborRX, neighborRY, neighborRZ) ~= nil
end
function Chunk:IsBlockRenderable(rx, ry, rz)
task.desynchronize()
local gx, gy, gz = self.pos.X, self.pos.Y, self.pos.Z
-- Check all six neighboring blocks
local d = not (
self:DoesNeighboringBlockExist(rx, ry, rz, gx, gy, gz, 1, 0, 0) and
self:DoesNeighboringBlockExist(rx, ry, rz, gx, gy, gz, -1, 0, 0) and
self:DoesNeighboringBlockExist(rx, ry, rz, gx, gy, gz, 0, 1, 0) and
self:DoesNeighboringBlockExist(rx, ry, rz, gx, gy, gz, 0, -1, 0) and
self:DoesNeighboringBlockExist(rx, ry, rz, gx, gy, gz, 0, 0, 1) and
self:DoesNeighboringBlockExist(rx, ry, rz, gx, gy, gz, 0, 0, -1)
)
return d
end
function Chunk:Tick()
self.inhabitedTime = tick()
end
function Chunk:PropogateChanges(x: number,y: number,z: number,d:BlockData)
self.UpdateBlockBindableL:Fire(x,y,z,d)
end
function Chunk:GetBlockAt(x,y,z)
task.desynchronize()
if not self.data[`{tostring(x)},{tostring(y)},{tostring(z)}`] then
return nil
end
return self.data[`{tostring(x)},{tostring(y)},{tostring(z)}`]
end
function Chunk:CreateBlock(x: number,y: number,z: number,d:BlockData)
self.data[`{tostring(x)},{tostring(y)},{tostring(z)}`] = d
self:PropogateChanges(x,y,z,d)
return self:GetBlockAt(x,y,z)
end
function Chunk:RemoveBlock(x, y, z)
self.data[x .. "," .. y .. "," .. z] = nil
self:PropogateChanges(x,y,z,0)
end
function Chunk:Load()
local i = sel
end
function Chunk:Unload()
task.synchronize()
self.loaded = false
-- SLOWCLEAR
task.defer(function()
local g = 0
for _,v in pairs(self.instance:GetChildren()) do
pcall(function()
v:Destroy()
end)
g += 1
if g == 30 then
g = 0
Swait(2)
end
end
task.synchronize()
self.instance.Parent = nil
self.instance:Destroy()
self.unloadChunkHook()
task.desynchronize()
end)
end
-- DO NOT INTERACT WITH CHUNK AFTER CALLING THIS
function Chunk:Destroy()
self.data = {}
end
return Chunk :: typeof(Chunk)

View File

@@ -0,0 +1,227 @@
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 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
task.synchronize()
if c:IsBlockRenderable(x, y, z) then
if c.instance:FindFirstChild(`{x},{y},{z}`) then return end
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
else
if c.instance:FindFirstChild(`{x},{y},{z}`) then
c.instance:FindFirstChild(`{x},{y},{z}`):Destroy()
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("")]: Chunk.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({{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}}) 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
if d == 0 then
if ch:FindFirstChild(`{x},{y},{z}`) then
task.synchronize()
ch:FindFirstChild(`{x},{y},{z}`):Destroy()
task.desynchronize()
end
return
end
if not c:IsBlockRenderable(x, y, z) then
if ch:FindFirstChild(`{x},{y},{z}`) then
task.synchronize()
ch:FindFirstChild(`{x},{y},{z}`):Destroy()
task.desynchronize()
end
return
end
if ch:FindFirstChild(`{x},{y},{z}`) then
task.synchronize()
ch:FindFirstChild(`{x},{y},{z}`):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 = `{x},{y},{z}`
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 a,b in pairs(newcache) do
for _, o in pairs({{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}}) do
-- chunks are 8x8x8
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 == 0 then ch.x = c.pos.X - 1 b.x = 8 end
if b.x == 9 then ch.x = c.pos.X + 1 b.x = 1 end
if b.y == 0 then ch.y = c.pos.Y - 1 b.y = 8 end
if b.y == 9 then ch.y = c.pos.Y +1 b.y = 1 end
if b.z == 0 then ch.z = c.pos.Z - 1 b.z = 8 end
if b.z == 9 then ch.z = c.pos.Z + 1 b.z = 1 end
propogateNeighboringBlockChanges(ch.x,ch.y,ch.z,b.x,b.y,b.z)
end
local coords = util.BlockPosStringToCoords(a)
if not c:IsBlockRenderable(coords.X, coords.Y, coords.Z) then
if ch:FindFirstChild(a) then
ch:FindFirstChild(a):Destroy()
end
continue
end
if ch:FindFirstChild(a) then
ch:FindFirstChild(a):Destroy()
end
local N = util.RotationStringToNormalId(b.state["r"] or "f")
local block = BlockManager:GetBlockRotated(b.id,N,b.state)
block.Name = a
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

View File

@@ -0,0 +1,208 @@
local ChunkManager = {}
local RunService = game:GetService("RunService")
local Chunk = require("./ChunkManager/Chunk")
local BlockManager = require("./ChunkManager/BlockManager")
local ChunkBuilder = require("./ChunkManager/ChunkBuilder")
local remote = game:GetService("ReplicatedStorage"):WaitForChild("RecieveChunkPacket")
local tickremote = game:GetService("ReplicatedStorage"):WaitForChild("Tick")
local ChunkFolder = Instance.new("Folder")
ChunkFolder.Name = "$blockscraft_client"
ChunkManager.ChunkFolder = ChunkFolder
local CHUNK_RADIUS = 5
local FORCELOAD_CHUNKS = {
{0, 1, 0}
}
local unloadingChunks = {}
local function Swait(l)
task.synchronize()
for _ = 1, l do
RunService.Stepped:Wait()
end
end
function ChunkManager:GetChunk(x, y, z)
local key = `{x},{y},{z}`
if Chunk.AllChunks[key] then
return Chunk.AllChunks[key]
end
task.synchronize()
local data = remote:InvokeServer(x, y, z)
task.desynchronize()
local ch = Chunk.from(x, y, z, data)
Chunk.AllChunks[key] = ch
return ch
end
local function ensureNeighboringChunksLoaded(x, y, z)
local offsets = {
{1, 0, 0}, {-1, 0, 0},
{0, 1, 0}, {0, -1, 0},
{0, 0, 1}, {0, 0, -1}
}
for _, offset in ipairs(offsets) do
local nx, ny, nz = x + offset[1], y + offset[2], z + offset[3]
ChunkManager:GetChunk(nx, ny, nz):Tick()
end
end
function ChunkManager:LoadChunk(x, y, z)
local key = `{x},{y},{z}`
if unloadingChunks[key] or not Chunk.AllChunks[key] or Chunk.AllChunks[key].loaded then
return
end
unloadingChunks[key] = true
task.defer(function()
task.desynchronize()
ensureNeighboringChunksLoaded(x, y, z)
local chunk = Chunk.AllChunks[key]
if not chunk then
chunk = ChunkManager:GetChunk(x, y, z)
Chunk.AllChunks[key] = chunk
end
task.synchronize()
local instance = ChunkBuilder:BuildChunk(chunk, ChunkFolder)
chunk.instance = instance
chunk.loaded = true
unloadingChunks[key] = nil
end)
end
function ChunkManager:ForceTick()
for _, coords in ipairs(FORCELOAD_CHUNKS) do
local key = `{coords[1]},{coords[2]},{coords[3]}`
local chunk = Chunk.AllChunks[key]
if not chunk then
ChunkManager:LoadChunk(coords[1], coords[2], coords[3])
else
chunk:Tick()
end
end
end
function ChunkManager:TickI()
for key, chunk in pairs(Chunk.AllChunks) do
if tick() - chunk.inhabitedTime <= 5 then
tickremote:FireServer(key)
end
end
end
function ChunkManager:Tick()
ChunkManager:ForceTick()
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)
}
task.defer(function()
for y = -CHUNK_RADIUS, CHUNK_RADIUS do
task.defer(function()
for x = -CHUNK_RADIUS, CHUNK_RADIUS do
task.desynchronize()
for z = -CHUNK_RADIUS, CHUNK_RADIUS do
local cx, cy, cz = chunkPos.x + x, chunkPos.y + y, chunkPos.z + z
local key = `{cx},{cy},{cz}`
local chunk = ChunkManager:GetChunk(cx, cy, cz)
chunk.inhabitedTime = tick()
if not chunk.loaded then
ChunkManager:LoadChunk(cx, cy, cz)
Swait(2)
end
end
task.synchronize()
end
end)
Swait(10)
end
end)
--[[
task.defer(function()
for y = 0, 2 do
task.defer(function()
for x = -CHUNK_RADIUS, CHUNK_RADIUS do
task.desynchronize()
for z = -CHUNK_RADIUS, CHUNK_RADIUS do
local cx, cy, cz = chunkPos.x + x, y, chunkPos.z + z
local key = `{cx},{cy},{cz}`
local chunk = ChunkManager:GetChunk(cx, cy, cz)
chunk.inhabitedTime = tick()
if not chunk.loaded then
ChunkManager:LoadChunk(cx, cy, cz)
Swait(2)
end
end
task.synchronize()
end
end)
Swait(10)
end
end)
--]]
for key, loadedChunk in pairs(Chunk.AllChunks) do
if tick() - loadedChunk.inhabitedTime > 15 and not unloadingChunks[key] then
unloadingChunks[key] = true
task.defer(function()
task.synchronize()
loadedChunk:Unload()
loadedChunk:Destroy()
Chunk.AllChunks[key] = nil
unloadingChunks[key] = nil
end)
end
end
end
function ChunkManager:Init()
if not RunService:IsClient() then
error("ChunkManager:Init can only be called on the client")
end
ChunkFolder.Parent = game:GetService("Workspace")
ChunkManager:ForceTick()
task.defer(function()
while true do
wait(2)
ChunkManager:TickI()
end
end)
task.defer(function()
while true do
task.defer(function()
local success, err = pcall(function()
ChunkManager:Tick()
end)
if not success then
warn("[CHUNKMANAGER]", err)
end
end)
Swait(20)
end
end)
end
return ChunkManager

View File

@@ -0,0 +1,47 @@
local ML = {}
type modContext = {
name: string,
description: string,
ns: string,
author: {string},
init: typeof(function()end)
}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Shared = ReplicatedStorage:WaitForChild("Shared")
local ModsFolder = ReplicatedStorage:WaitForChild("Mods")
function ML.loadModsS()
print("[SSModLoader] Loading Mods")
for _, m in pairs(ModsFolder:GetChildren()) do
local success, reason = pcall(function()
-- ignore type err
local mod: modContext = require(m)
mod.init()
print(`[SSModLoader] Loaded {mod.name} ({mod.ns}) by {table.concat(mod.author,", ")}`)
end)
if not success then
warn(`[CSModLoader] Error loading {m.Name}: {reason}`)
end
end
end
function ML.loadModsC()
print("[CSModLoader] Loading Mods")
for _, m in pairs(ModsFolder:GetChildren()) do
local success, reason = pcall(function()
-- ignore type err
local mod: modContext = require(m)
mod.init()
print(`[CSModLoader] Loaded {mod.name} ({mod.ns}) by {table.concat(mod.author,", ")}`)
end)
if not success then
warn(`[CSModLoader] Error loading {m.Name}: {reason}`)
end
end
end
return ML

View File

@@ -0,0 +1,140 @@
local PlacementManager = {}
local ChunkManager = require("./ChunkManager")
local Util = require("./Util")
PlacementManager.ChunkFolder = ChunkManager.ChunkFolder
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {PlacementManager.ChunkFolder}
raycastParams.FilterType = Enum.RaycastFilterType.Include
raycastParams.IgnoreWater = true
if _G.SB then return nil end
_G.SB = true
PlacementManager.SelectionBox = script.SelectionBox:Clone()
PlacementManager.SelectionBox.Name = "$SelectionBox"..(game:GetService("RunService"):IsServer() and "_SERVER" or "")
PlacementManager.SelectionBox.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
-- Trash method TODO: Fix this
local function findParent(i: Instance): Instance
local f = i:FindFirstAncestorOfClass("Folder")
local d = i
repeat
d = d.Parent
until d.Parent == f
return d
end
local Mouse: Mouse = nil
-- Gets the block and normalid of the block (and surface) the player is looking at
function PlacementManager:Raycast()
if not Mouse then
Mouse = game:GetService("Players").LocalPlayer:GetMouse()
end
task.synchronize()
local objLookingAt = Mouse.Target
local dir = Mouse.TargetSurface
if not objLookingAt then
PlacementManager.SelectionBox.Adornee = nil
script.RaycastResult.Value = nil
return
end
--if not objLookingAt:IsDescendantOf(ChunkManager.ChunkFolder) then return end
local parent = findParent(objLookingAt)
if parent:GetAttribute("ns") == true then
PlacementManager.SelectionBox.Adornee = nil
script.RaycastResult.Value = nil
return
end
PlacementManager.SelectionBox.Adornee = parent
script.RaycastResult.Value = parent
return parent, dir
end
function PlacementManager:RaycastGetResult()
return script.RaycastResult.Value
end
local placeRemote = game:GetService("ReplicatedStorage").PlaceBlock
local breakRemote = game:GetService("ReplicatedStorage").BreakBlock
local tickRemote = game:GetService("ReplicatedStorage").Tick
-- FIRES REMOTE
function PlacementManager:PlaceBlock(cx, cy, cz, x, y, z, blockData)
--print("placeblock")
--local chunk = ChunkManager:GetChunk(cx, cy, cz)
--chunk:CreateBlock(x, y, z, blockData)
placeRemote:FireServer(cx, cy, cz, x, y, z, blockData)
end
-- FIRES REMOTE
function PlacementManager:BreakBlock(cx, cy, cz, x, y, z)
--print("breakblock")
--local chunk = ChunkManager:GetChunk(cx, cy, cz)
--chunk:RemoveBlock(x, y, z)
breakRemote:FireServer(cx, cy, cz, x, y, z)
end
-- CLIENTSIDED
function PlacementManager:PlaceBlockLocal(cx, cy, cz, x, y, z, blockData)
local chunk = ChunkManager:GetChunk(cx, cy, cz)
chunk:CreateBlock(x, y, z, blockData)
end
-- CLIENTSIDED
function PlacementManager:BreakBlockLocal(cx, cy, cz, x, y, z)
local chunk = ChunkManager:GetChunk(cx, cy, cz)
chunk:RemoveBlock(x, y, z)
end
function PlacementManager:GetBlockAtMouse(): nil | {chunk:Vector3, block: Vector3}
local selectedPart = PlacementManager:RaycastGetResult()
--print(selectedPart and selectedPart:GetFullName() or nil)
if selectedPart == nil then
PlacementManager.SelectionBox.Adornee = nil
script.RaycastResult.Value = nil
return nil
end
if not selectedPart.Parent then
PlacementManager.SelectionBox.Adornee = nil
script.RaycastResult.Value = nil
return nil
end
local chunkCoords = Util.BlockPosStringToCoords(selectedPart.Parent.Name)
local blockCoords = Util.BlockPosStringToCoords(selectedPart.Name)
return {
chunk = chunkCoords,
block = blockCoords
}
end
function PlacementManager:Init()
game:GetService("RunService").Heartbeat:Connect(function()
local a,b = pcall(function()
PlacementManager:Raycast()
end)
if not a then
task.synchronize()
PlacementManager.SelectionBox.Adornee = nil
script.RaycastResult.Value = nil
task.desynchronize()
end
end)
tickRemote.OnClientEvent:Connect(function(m, cx, cy, cz, x, y, z, d)
--warn("PROPOGATED TICK", m, cx, cy, cz, x, y, z, d)
if m == "B_C" then
PlacementManager:PlaceBlockLocal(cx, cy, cz, x, y ,z, d)
end
if m == "B_D" then
PlacementManager:BreakBlockLocal(cx, cy, cz, x, y ,z)
end
end)
end
return PlacementManager

View File

@@ -0,0 +1,3 @@
{
"ignoreUnknownInstances": true
}

View File

@@ -0,0 +1,48 @@
local module = {}
function module.BlockPosStringToCoords(s: string): Vector3
-- a,b,c
local split = string.split(s,",")
return Vector3.new(tonumber(split[1]), tonumber(split[2]), tonumber(split[3]))
end
function module.RotationStringToNormalId(s: string): Enum.NormalId
if not s then return Enum.NormalId.Front end
if s == "f" then
return Enum.NormalId.Front
end
if s == "b" then
return Enum.NormalId.Back
end
if s == "l" then
return Enum.NormalId.Left
end
if s == "r" then
return Enum.NormalId.Right
end
if s == "t" then
return Enum.NormalId.Top
end
if s == "b" then
return Enum.NormalId.Bottom
end
warn("Could not convert",s,"to Enum.NormalId")
return Enum.NormalId.Front
end
-- chunk size 8x8x8 relative block indexs 1-8
function module.GlobalBlockPosToRelative(x: number,y:number,z:number)
return x%8,y%8,z%8
end
function module.ChunkPosToCFrame(chunk: Vector3, block: Vector3): CFrame
return CFrame.new(
CFrame.new(
(32*chunk.X)+(block.X*4)-18,
(32*chunk.Y)+(block.Y*4)-18,
(32*chunk.Z)+(block.Z*4)-18
).Position
)
end
return module

View File

@@ -0,0 +1,3 @@
{
"ignoreUnknownInstances": true
}