From 2583f46d7d14a6a276ed1be27e3a5a125d7b6696 Mon Sep 17 00:00:00 2001 From: OCbwoy3 Date: Tue, 6 Jan 2026 07:56:21 +0200 Subject: [PATCH] chore: init from rojo-to-rbxlx --- default.project.json | 35 + place.rbxl | Bin 0 -> 269616 bytes place.rbxl.lock | 5 + .../Shared/ChunkManager/BlockManager.lua | 86 + .../ChunkManager/BlockManager.meta.json | 3 + .../Shared/ChunkManager/Chunk.lua | 180 ++ .../Shared/ChunkManager/ChunkBuilder.lua | 227 +++ .../Shared/ChunkManager/init.lua | 208 ++ src/ReplicatedStorage/Shared/ModLoader.lua | 47 + .../Shared/PlacementManager.lua | 140 ++ .../Shared/PlacementManager.meta.json | 3 + src/ReplicatedStorage/Shared/Util.lua | 48 + src/ReplicatedStorage/Shared/init.meta.json | 3 + .../TerrainGen/Deflate/Huffman/BitBuffer.lua | 1816 +++++++++++++++++ .../TerrainGen/Deflate/Huffman/Node.lua | 38 + .../Deflate/Huffman/PriorityQueue.lua | 232 +++ .../TerrainGen/Deflate/Huffman/init.lua | 114 ++ .../TerrainGen/Deflate/LZW.lua | 170 ++ .../TerrainGen/Deflate/init.lua | 19 + .../ServerChunkManager/TerrainGen/init.lua | 65 + .../Actor/ServerChunkManager/init.server.lua | 107 + src/ServerScriptService/Actor/init.meta.json | 4 + .../Crosshair/LocalScript.client.lua | 22 + src/StarterGui/Crosshair/init.meta.json | 4 + src/StarterGui/Game_UI/LocalScript.client.lua | 79 + src/StarterGui/Game_UI/init.meta.json | 4 + .../Actor/Init.client.lua | 25 + .../StarterPlayerScripts/Actor/init.meta.json | 4 + src/Workspace/mods/init.meta.json | 3 + src/Workspace/mods/mc/init.lua | 23 + src/Workspace/mods/mc/init.meta.json | 3 + .../mods/mc/upds/BlockUpdateOperation.lua | 3 + src/Workspace/mods/mc/upds/init.meta.json | 3 + 33 files changed, 3723 insertions(+) create mode 100644 default.project.json create mode 100644 place.rbxl create mode 100644 place.rbxl.lock create mode 100644 src/ReplicatedStorage/Shared/ChunkManager/BlockManager.lua create mode 100644 src/ReplicatedStorage/Shared/ChunkManager/BlockManager.meta.json create mode 100644 src/ReplicatedStorage/Shared/ChunkManager/Chunk.lua create mode 100644 src/ReplicatedStorage/Shared/ChunkManager/ChunkBuilder.lua create mode 100644 src/ReplicatedStorage/Shared/ChunkManager/init.lua create mode 100644 src/ReplicatedStorage/Shared/ModLoader.lua create mode 100644 src/ReplicatedStorage/Shared/PlacementManager.lua create mode 100644 src/ReplicatedStorage/Shared/PlacementManager.meta.json create mode 100644 src/ReplicatedStorage/Shared/Util.lua create mode 100644 src/ReplicatedStorage/Shared/init.meta.json create mode 100644 src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/BitBuffer.lua create mode 100644 src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/Node.lua create mode 100644 src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/PriorityQueue.lua create mode 100644 src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/init.lua create mode 100644 src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/LZW.lua create mode 100644 src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/init.lua create mode 100644 src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/init.lua create mode 100644 src/ServerScriptService/Actor/ServerChunkManager/init.server.lua create mode 100644 src/ServerScriptService/Actor/init.meta.json create mode 100644 src/StarterGui/Crosshair/LocalScript.client.lua create mode 100644 src/StarterGui/Crosshair/init.meta.json create mode 100644 src/StarterGui/Game_UI/LocalScript.client.lua create mode 100644 src/StarterGui/Game_UI/init.meta.json create mode 100644 src/StarterPlayer/StarterPlayerScripts/Actor/Init.client.lua create mode 100644 src/StarterPlayer/StarterPlayerScripts/Actor/init.meta.json create mode 100644 src/Workspace/mods/init.meta.json create mode 100644 src/Workspace/mods/mc/init.lua create mode 100644 src/Workspace/mods/mc/init.meta.json create mode 100644 src/Workspace/mods/mc/upds/BlockUpdateOperation.lua create mode 100644 src/Workspace/mods/mc/upds/init.meta.json diff --git a/default.project.json b/default.project.json new file mode 100644 index 0000000..bddf56a --- /dev/null +++ b/default.project.json @@ -0,0 +1,35 @@ +{ + "name": "project", + "tree": { + "$className": "DataModel", + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "$ignoreUnknownInstances": true, + "$path": "src/ReplicatedStorage" + }, + "ServerScriptService": { + "$className": "ServerScriptService", + "$ignoreUnknownInstances": true, + "$path": "src/ServerScriptService" + }, + "StarterGui": { + "$className": "StarterGui", + "$ignoreUnknownInstances": true, + "$path": "src/StarterGui" + }, + "StarterPlayer": { + "$className": "StarterPlayer", + "StarterPlayerScripts": { + "$className": "StarterPlayerScripts", + "$ignoreUnknownInstances": true, + "$path": "src/StarterPlayer/StarterPlayerScripts" + }, + "$ignoreUnknownInstances": true + }, + "Workspace": { + "$className": "Workspace", + "$ignoreUnknownInstances": true, + "$path": "src/Workspace" + } + } +} \ No newline at end of file diff --git a/place.rbxl b/place.rbxl new file mode 100644 index 0000000000000000000000000000000000000000..ef89c5004333d5ea696dad2e4630b94baf3a3397 GIT binary patch literal 269616 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 z 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%0Dris50Ttmg0|wz~E0~0* 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�jOBfpHiDM98#MSj+AQ*SU@A1R3U$1AFjY< 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 literal 0 HcmV?d00001 diff --git a/place.rbxl.lock b/place.rbxl.lock new file mode 100644 index 0000000..f2398db --- /dev/null +++ b/place.rbxl.lock @@ -0,0 +1,5 @@ +292 +RobloxStudioBeta +CACHYOS +3ad4fa05-1765-4ae8-a484-86d03d176c98 + diff --git a/src/ReplicatedStorage/Shared/ChunkManager/BlockManager.lua b/src/ReplicatedStorage/Shared/ChunkManager/BlockManager.lua new file mode 100644 index 0000000..002c9d3 --- /dev/null +++ b/src/ReplicatedStorage/Shared/ChunkManager/BlockManager.lua @@ -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 diff --git a/src/ReplicatedStorage/Shared/ChunkManager/BlockManager.meta.json b/src/ReplicatedStorage/Shared/ChunkManager/BlockManager.meta.json new file mode 100644 index 0000000..1025b06 --- /dev/null +++ b/src/ReplicatedStorage/Shared/ChunkManager/BlockManager.meta.json @@ -0,0 +1,3 @@ +{ + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/ReplicatedStorage/Shared/ChunkManager/Chunk.lua b/src/ReplicatedStorage/Shared/ChunkManager/Chunk.lua new file mode 100644 index 0000000..490d9d2 --- /dev/null +++ b/src/ReplicatedStorage/Shared/ChunkManager/Chunk.lua @@ -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) diff --git a/src/ReplicatedStorage/Shared/ChunkManager/ChunkBuilder.lua b/src/ReplicatedStorage/Shared/ChunkManager/ChunkBuilder.lua new file mode 100644 index 0000000..8c49c9c --- /dev/null +++ b/src/ReplicatedStorage/Shared/ChunkManager/ChunkBuilder.lua @@ -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 diff --git a/src/ReplicatedStorage/Shared/ChunkManager/init.lua b/src/ReplicatedStorage/Shared/ChunkManager/init.lua new file mode 100644 index 0000000..e076b31 --- /dev/null +++ b/src/ReplicatedStorage/Shared/ChunkManager/init.lua @@ -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 diff --git a/src/ReplicatedStorage/Shared/ModLoader.lua b/src/ReplicatedStorage/Shared/ModLoader.lua new file mode 100644 index 0000000..7049b8d --- /dev/null +++ b/src/ReplicatedStorage/Shared/ModLoader.lua @@ -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 diff --git a/src/ReplicatedStorage/Shared/PlacementManager.lua b/src/ReplicatedStorage/Shared/PlacementManager.lua new file mode 100644 index 0000000..7ce9846 --- /dev/null +++ b/src/ReplicatedStorage/Shared/PlacementManager.lua @@ -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 diff --git a/src/ReplicatedStorage/Shared/PlacementManager.meta.json b/src/ReplicatedStorage/Shared/PlacementManager.meta.json new file mode 100644 index 0000000..1025b06 --- /dev/null +++ b/src/ReplicatedStorage/Shared/PlacementManager.meta.json @@ -0,0 +1,3 @@ +{ + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/ReplicatedStorage/Shared/Util.lua b/src/ReplicatedStorage/Shared/Util.lua new file mode 100644 index 0000000..15b9fab --- /dev/null +++ b/src/ReplicatedStorage/Shared/Util.lua @@ -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 diff --git a/src/ReplicatedStorage/Shared/init.meta.json b/src/ReplicatedStorage/Shared/init.meta.json new file mode 100644 index 0000000..1025b06 --- /dev/null +++ b/src/ReplicatedStorage/Shared/init.meta.json @@ -0,0 +1,3 @@ +{ + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/BitBuffer.lua b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/BitBuffer.lua new file mode 100644 index 0000000..a854c6c --- /dev/null +++ b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/BitBuffer.lua @@ -0,0 +1,1816 @@ +local CHAR_SET = [[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/]] + +-- Tradition is to use chars for the lookup table instead of codepoints. +-- But due to how we're running the encode function, it's faster to use codepoints. +local encode_char_set = {} +local decode_char_set = {} +for i = 1, 64 do + encode_char_set[i - 1] = string.byte(CHAR_SET, i, i) + decode_char_set[string.byte(CHAR_SET, i, i)] = i - 1 +end + +-- stylua: ignore +local HEX_TO_BIN = { + ["0"] = "0000", ["1"] = "0001", ["2"] = "0010", ["3"] = "0011", + ["4"] = "0100", ["5"] = "0101", ["6"] = "0110", ["7"] = "0111", + ["8"] = "1000", ["9"] = "1001", ["a"] = "1010", ["b"] = "1011", + ["c"] = "1100", ["d"] = "1101", ["e"] = "1110", ["f"] = "1111" +} + +-- stylua: ignore +local NORMAL_ID_VECTORS = { -- [Enum.Value] = Vector3.fromNormalId(Enum) + [0] = Vector3.new(1, 0, 0), -- Enum.NormalId.Right + [1] = Vector3.new(0, 1, 0), -- Enum.NormalId.Top + [2] = Vector3.new(0, 0, 1), -- Enum.NormalId.Back + [3] = Vector3.new(-1, 0, 0), -- Enum.NormalId.Left + [4] = Vector3.new(0, -1, 0), -- Enum.NormalId.Bottom + [5] = Vector3.new(0, 0, -1) -- Enum.NormalId.Front +} + +local ONES_VECTOR = Vector3.new(1, 1, 1) + +local BOOL_TO_BIT = { [true] = 1, [false] = 0 } + +local CRC32_POLYNOMIAL = 0xedb88320 + +local crc32_poly_lookup = {} +for i = 0, 255 do + local crc = i + for _ = 1, 8 do + local mask = -bit32.band(crc, 1) + crc = bit32.bxor(bit32.rshift(crc, 1), bit32.band(CRC32_POLYNOMIAL, mask)) + end + crc32_poly_lookup[i] = crc +end + +local powers_of_2 = {} +for i = 0, 64 do + powers_of_2[i] = 2 ^ i +end + +local byte_to_hex = {} +for i = 0, 255 do + byte_to_hex[i] = string.format("%02x", i) +end + +local function bitBuffer(stream) + if stream ~= nil then + assert(type(stream) == "string", "argument to BitBuffer constructor must be either nil or a string") + end + + -- The bit buffer works by keeping an array of bytes, a 'final' byte, and how many bits are currently in that last byte + -- Bits are not kept track of on their own, and are instead combined to form a byte, which is stored in the last space in the array. + -- This byte is also stored seperately, so that table operations aren't needed to read or modify its value. + -- The byte array is called `bytes`. The last byte is stored in `lastByte`. The bit counter is stored in `bits`. + + local bits = 0 -- How many free floating bits there are. + local bytes = {} --! -- Array of bytes currently in the buffer + local lastByte = 0 -- The most recent byte in the buffer, made up of free floating bits + + local byteCount = 0 -- This variable keeps track of how many bytes there are total in the bit buffer. + local bitCount = 0 -- This variable keeps track of how many bits there are total in the bit buffer + + local pointer = 0 -- This variable keeps track of what bit the read functions start at + local pointerByte = 1 -- This variable keeps track of what byte the pointer is at. It starts at 1 since the byte array starts at 1. + + if stream then + byteCount = #stream + bitCount = byteCount * 8 + + bytes = table.create(#stream) + + for i = 1, byteCount do + bytes[i] = string.byte(stream, i, i) + end + end + + local function dumpBinary() + -- This function is for debugging or analysis purposes. + -- It dumps the contents of the byte array and the remaining bits into a string of binary digits. + -- Thus, bytes [97, 101] with bits [1, 1, 0] would output "01100001 01100101 110" + local output = table.create(byteCount) --! + for i, v in ipairs(bytes) do + output[i] = string.gsub(byte_to_hex[v], "%x", HEX_TO_BIN) + end + if bits ~= 0 then + -- Because the last byte (where the free floating bits are stored) is in the byte array, it has to be overwritten. + output[byteCount] = string.sub(output[byteCount], 1, bits) + end + + return table.concat(output, " ") + end + + local function dumpStringOld() + -- This function is for accessing the total contents of the bitbuffer. + -- This function combines all the bytes, including the last byte, into a string of binary data. + -- Thus, bytes [97, 101] and bits [1, 1, 0] would become (in hex) "0x61 0x65 0x06" + + -- It's substantially faster to create several smaller strings before using table.concat. (well maybe it was, but it isn't now post 2022) + local output = table.create(math.ceil(byteCount / 4096)) --! + local c = 1 + for i = 1, byteCount, 4096 do -- groups of 4096 bytes is the point at which there are diminishing returns + output[c] = string.char(table.unpack(bytes, i, math.min(byteCount, i + 4095))) + c = c + 1 + end + + return table.concat(output, "") + end + + --Let lua be lua + local function dumpString() + return string.char(table.unpack(bytes)) + end + + + local function dumpHex() + -- This function is for getting the hex of the bitbuffer's contents, should that be desired + local output = table.create(byteCount) --! + for i, v in ipairs(bytes) do + output[i] = byte_to_hex[v] + end + + return table.concat(output, "") + end + + local function dumpBase64() + -- Base64 is a safe and easy way to convert binary data to be entirely printable + -- It works on the principle that groups of 3 bytes (24 bits) can evenly be divided into 4 groups of 6 + -- And 2^6 is a mere 64, far less than the number of printable characters. + -- If there are any missing bytes, `=` is added to the end as padding. + -- Base64 increases the size of its input by 33%. + local output = table.create(math.ceil(byteCount * 1.333)) --! + + local c = 1 + for i = 1, byteCount, 3 do + local b1, b2, b3 = bytes[i], bytes[i + 1], bytes[i + 2] + local packed = bit32.bor(bit32.lshift(b1, 16), bit32.lshift(b2 or 0, 8), b3 or 0) + + -- This can be done with bit32.extract (and/or bit32.lshift, bit32.band, bit32.rshift) + -- But bit masking and shifting is more eloquent in my opinion. + output[c] = encode_char_set[bit32.rshift(bit32.band(packed, 0xfc0000), 0x12)] + output[c + 1] = encode_char_set[bit32.rshift(bit32.band(packed, 0x3f000), 0xc)] + output[c + 2] = b2 and encode_char_set[bit32.rshift(bit32.band(packed, 0xfc0), 0x6)] or 0x3d -- 0x3d == "=" + output[c + 3] = b3 and encode_char_set[bit32.band(packed, 0x3f)] or 0x3d + + c = c + 4 + end + c = c - 1 -- c will always be 1 more than the length of `output` + + local realOutput = table.create(math.ceil(c / 0x1000)) --! + local k = 1 + for i = 1, c, 0x1000 do + realOutput[k] = string.char(table.unpack(output, i, math.min(c, i + 0xfff))) + k = k + 1 + end + + return table.concat(realOutput, "") + end + + local function exportChunk(chunkLength) + assert(type(chunkLength) == "number", "argument #1 to BitBuffer.exportChunk should be a number") + assert(chunkLength > 0, "argument #1 to BitBuffer.exportChunk should be above zero") + assert(chunkLength % 1 == 0, "argument #1 to BitBuffer.exportChunk should be an integer") + + -- Since `i` is being returned, the most eloquent way to handle this is with a coroutine + -- This allows returning the existing value of `i` without having to increment it first. + -- The alternative was starting at `i = -(chunkLength-1)` and incrementing at the start of the iterator function. + return coroutine.wrap(function() + local realChunkLength = chunkLength - 1 + -- Since this function only has one 'state', it's perfectly fine to use a for-loop. + for i = 1, byteCount, chunkLength do + local chunk = string.char(table.unpack(bytes, i, math.min(byteCount, i + realChunkLength))) + coroutine.yield(i, chunk) + end + end) + end + + local function exportBase64Chunk(chunkLength) + chunkLength = chunkLength or 76 + assert(type(chunkLength) == "number", "argument #1 to BitBuffer.exportBase64Chunk should be a number") + assert(chunkLength > 0, "argument #1 to BitBuffer.exportBase64Chunk should be above zero") + assert(chunkLength % 1 == 0, "argument #1 to BitBuffer.exportBase64Chunk should be an integer") + + local output = table.create(math.ceil(byteCount * 0.333)) --! + + local c = 1 + for i = 1, byteCount, 3 do + local b1, b2, b3 = bytes[i], bytes[i + 1], bytes[i + 2] + local packed = bit32.bor(bit32.lshift(b1, 16), bit32.lshift(b2 or 0, 8), b3 or 0) + + output[c] = encode_char_set[bit32.rshift(bit32.band(packed, 0xfc0000), 0x12)] + output[c + 1] = encode_char_set[bit32.rshift(bit32.band(packed, 0x3f000), 0xc)] + output[c + 2] = b2 and encode_char_set[bit32.rshift(bit32.band(packed, 0xfc0), 0x6)] or 0x3d + output[c + 3] = b3 and encode_char_set[bit32.band(packed, 0x3f)] or 0x3d + + c = c + 4 + end + c = c - 1 + + return coroutine.wrap(function() + local realChunkLength = chunkLength - 1 + for i = 1, c, chunkLength do + local chunk = string.char(table.unpack(output, i, math.min(c, i + realChunkLength))) + coroutine.yield(chunk) + end + end) + end + + local function exportHexChunk(chunkLength) + assert(type(chunkLength) == "number", "argument #1 to BitBuffer.exportHexChunk should be a number") + assert(chunkLength > 0, "argument #1 to BitBuffer.exportHexChunk should be above zero") + assert(chunkLength % 1 == 0, "argument #1 to BitBuffer.exportHexChunk should be an integer") + + local halfLength = math.floor(chunkLength / 2) + + if chunkLength % 2 == 0 then + return coroutine.wrap(function() + local output = {} --! + for i = 1, byteCount, halfLength do + for c = 0, halfLength - 1 do + output[c] = byte_to_hex[bytes[i + c]] + end + coroutine.yield(table.concat(output, "", 0)) + end + end) + else + return coroutine.wrap(function() + local output = { [0] = "" } --! + local remainder = "" + + local i = 1 + while i <= byteCount do + if remainder == "" then + output[0] = "" + for c = 0, halfLength - 1 do + output[c + 1] = byte_to_hex[bytes[i + c]] + end + local endByte = byte_to_hex[bytes[i + halfLength]] + if endByte then + output[halfLength + 1] = string.sub(endByte, 1, 1) + remainder = string.sub(endByte, 2, 2) + end + i = i + 1 + else + output[0] = remainder + for c = 0, halfLength - 1 do + output[c + 1] = byte_to_hex[bytes[i + c]] + end + output[halfLength + 1] = "" + remainder = "" + end + + coroutine.yield(table.concat(output, "", 0)) + i = i + halfLength + end + end) + end + end + + local function crc32() + local crc = 0xffffffff -- 2^32 + + for _, v in ipairs(bytes) do + local poly = crc32_poly_lookup[bit32.band(bit32.bxor(crc, v), 255)] + crc = bit32.bxor(bit32.rshift(crc, 8), poly) + end + + return bit32.bnot(crc) % 0xffffffff -- 2^32 + end + + local function getLength() + return bitCount + end + + local function getByteLength() + return byteCount + end + + local function getPointer() + -- This function gets the value of the pointer. This is self-explanatory. + return pointer + end + + local function setPointer(n) + assert(type(n) == "number", "argument #1 to BitBuffer.setPointer should be a number") + assert(n >= 0, "argument #1 to BitBuffer.setPointer should be zero or higher") + assert(n % 1 == 0, "argument #1 to BitBuffer.setPointer should be an integer") + assert(n <= bitCount, "argument #1 to BitBuffer.setPointerByte should within range of the buffer") + -- This function sets the value of pointer. This is self-explanatory. + pointer = n + pointerByte = math.floor(n / 8) + 1 + end + + local function setPointerFromEnd(n) + assert(type(n) == "number", "argument #1 to BitBuffer.setPointerFromEnd should be a number") + assert(n >= 0, "argument #1 to BitBuffer.setPointerFromEnd should be zero or higher") + assert(n % 1 == 0, "argument #1 to BitBuffer.setPointerFromEnd should be an integer") + assert(n <= bitCount, "argument #1 to BitBuffer.setPointerFromEnd should within range of the buffer") + + pointer = bitCount - n + pointerByte = math.floor(pointer / 8 + 1) + end + + local function getPointerByte() + return pointerByte + end + + local function setPointerByte(n) + assert(type(n) == "number", "argument #1 to BitBuffer.setPointerByte should be a number") + assert(n > 0, "argument #1 to BitBuffer.setPointerByte should be positive") + assert(n % 1 == 0, "argument #1 to BitBuffer.setPointerByte should be an integer") + assert(n <= byteCount, "argument #1 to BitBuffer.setPointerByte should be within range of the buffer") + -- Sets the value of the pointer in bytes instead of bits + pointer = n * 8 + pointerByte = n + end + + local function setPointerByteFromEnd(n) + assert(type(n) == "number", "argument #1 to BitBuffer.setPointerByteFromEnd should be a number") + assert(n >= 0, "argument #1 to BitBuffer.setPointerByteFromEnd should be zero or higher") + assert(n % 1 == 0, "argument #1 to BitBuffer.setPointerByteFromEnd should be an integer") + assert(n <= byteCount, "argument #1 to BitBuffer.setPointerByteFromEnd should be within range of the buffer") + + pointerByte = byteCount - n + pointer = pointerByte * 8 + end + + local function isFinished() + return pointer == bitCount + end + + local function writeBits(...) + -- The first of two main functions for the actual 'writing' of the bitbuffer. + -- This function takes a vararg of 1s and 0s and writes them to the buffer. + local bitN = select("#", ...) + if bitN == 0 then + return + end -- Throwing here seems unnecessary + bitCount = bitCount + bitN + local packed = table.pack(...) + for _, v in ipairs(packed) do + assert(v == 1 or v == 0, "arguments to BitBuffer.writeBits should be either 1 or 0") + if bits == 0 then -- If the bit count is 0, increment the byteCount + -- This is the case at the beginning of the buffer as well as when the the buffer reaches 7 bits, + -- so it's done at the beginning of the loop. + byteCount = byteCount + 1 + end + lastByte = lastByte + (v == 1 and powers_of_2[7 - bits] or 0) -- Add the current bit to lastByte, from right to left + bits = bits + 1 + if bits == 8 then -- If the bit count is 8, set it to 0, write lastByte to the byte list, and set lastByte to 0 + bits = 0 + bytes[byteCount] = lastByte + lastByte = 0 + end + end + if bits ~= 0 then -- If there are some bits in lastByte, it has to be put into lastByte + -- If this is done regardless of the bit count, there might be a trailing zero byte + bytes[byteCount] = lastByte + end + end + + local function writeByte(n) + --assert(type(n) == "number", "argument #1 to BitBuffer.writeByte should be a number") + --assert(n >= 0 and n <= 255, "argument #1 to BitBuffer.writeByte should be in the range [0, 255]") + --assert(n % 1 == 0, "argument #1 to BitBuffer.writeByte should be an integer") + + -- The second of two main functions for the actual 'writing' of the bitbuffer. + -- This function takes a byte (an 8-bit integer) and writes it to the buffer. + if bits == 0 then + -- If there aren't any free-floating bits, this is easy. + byteCount = byteCount + 1 + bytes[byteCount] = n + else + local nibble = bit32.rshift(n, bits) -- Shift `bits` number of bits out of `n` (they go into the aether) + bytes[byteCount] = lastByte + nibble -- Manually set the most recent byte to the lastByte + the front part of `n` + byteCount = byteCount + 1 + lastByte = bit32.band(bit32.lshift(n, 8 - bits), 255) -- Shift `n` forward `8-bits` and get what remains in the first 8 bits + bytes[byteCount] = lastByte + end + bitCount = bitCount + 8 -- Increment the bit counter + end + + local function writeBytesFast(tab) + assert(bits == 0, "writeBytesFast can only work for whole byte streams") + local count = #tab + table.move(tab, 1 , count, byteCount + 1, bytes) + byteCount+= count + bitCount += count * 8 + end + + local function writeUnsigned(width, n) + assert(type(width) == "number", "argument #1 to BitBuffer.writeUnsigned should be a number") + assert(width >= 1 and width <= 64, "argument #1 to BitBuffer.writeUnsigned should be in the range [1, 64]") + assert(width % 1 == 0, "argument #1 to BitBuffer.writeUnsigned should be an integer") + + assert(type(n) == "number", "argument #2 to BitBuffer.writeUnsigned should be a number") + assert(n >= 0 and n <= powers_of_2[width] - 1, "argument #2 to BitBuffer.writeUnsigned is out of range") + assert(n % 1 == 0, "argument #2 to BitBuffer.writeUnsigned should be an integer") + -- Writes unsigned integers of arbitrary length to the buffer. + -- This is the first function that uses other functions in the buffer to function. + -- This is done because the space taken up would be rather large for very little performance gain. + + -- Get the number of bytes and number of floating bits in the specified width + local bytesInN, bitsInN = math.floor(width / 8), width % 8 + local extractedBits = table.create(bitsInN) --! + + -- If the width is less than or equal to 32-bits, bit32 can be used without any problem. + if width <= 32 then + -- Counting down from the left side, the bytes are written to the buffer + local c = width + for _ = 1, bytesInN do + c = c - 8 + writeByte(bit32.extract(n, c, 8)) + end + -- Any remaining bits are stored in an array + for i = bitsInN - 1, 0, -1 do + extractedBits[bitsInN - i] = BOOL_TO_BIT[bit32.btest(n, powers_of_2[i])] + end + -- Said array is then used to write them to the buffer + writeBits(table.unpack(extractedBits)) + else + -- If the width is greater than 32, the number has to be divided up into a few 32-bit or less numbers + local leastSignificantChunk = n % 0x100000000 -- Get bits 0-31 (counting from the right side). 0x100000000 is 2^32. + local mostSignificantChunk = math.floor(n / 0x100000000) -- Get any remaining bits by manually right shifting by 32 bits + + local c = width - 32 -- The number of bits in mostSignificantChunk is variable, but a counter is still needed + for _ = 1, bytesInN - 4 do -- 32 bits is 4 bytes + c = c - 8 + writeByte(bit32.extract(mostSignificantChunk, c, 8)) + end + -- `bitsInN` is always going to be the number of spare bits in `mostSignificantChunk` + -- which comes before `leastSignificantChunk` + for i = bitsInN - 1, 0, -1 do + extractedBits[bitsInN - i] = BOOL_TO_BIT[bit32.btest(mostSignificantChunk, powers_of_2[i])] + end + writeBits(table.unpack(extractedBits)) + + for i = 3, 0, -1 do -- Then of course, write all 4 bytes of leastSignificantChunk + writeByte(bit32.extract(leastSignificantChunk, i * 8, 8)) + end + end + end + + local function writeSigned(width, n) + assert(type(width) == "number", "argument #1 to BitBuffer.writeSigned should be a number") + assert(width >= 2 and width <= 64, "argument #1 to BitBuffer.writeSigned should be in the range [2, 64]") + assert(width % 1 == 0, "argument #1 to BitBuffer.writeSigned should be an integer") + + assert(type(n) == "number", "argument #2 to BitBuffer.writeSigned should be a number") + assert( + n >= -powers_of_2[width - 1] and n <= powers_of_2[width - 1] - 1, + "argument #2 to BitBuffer.writeSigned is out of range" + ) + assert(n % 1 == 0, "argument #2 to BitBuffer.writeSigned should be an integer") + -- Writes signed integers of arbitrary length to the buffer. + -- These integers are stored using two's complement. + -- Essentially, this means the first bit in the number is used to store whether it's positive or negative + -- If the number is positive, it's stored normally. + -- If it's negative, the number that's stored is equivalent to the max value of the width + the number + if n >= 0 then + writeBits(0) + writeUnsigned(width - 1, n) -- One bit is used for the sign, so the stored number's width is actually width-1 + else + writeBits(1) + writeUnsigned(width - 1, powers_of_2[width - 1] + n) + end + end + + local function writeFloat(exponentWidth, mantissaWidth, n) + assert(type(exponentWidth) == "number", "argument #1 to BitBuffer.writeFloat should be a number") + assert( + exponentWidth >= 1 and exponentWidth <= 64, + "argument #1 to BitBuffer.writeFloat should be in the range [1, 64]" + ) + assert(exponentWidth % 1 == 0, "argument #1 to BitBuffer.writeFloat should be an integer") + + assert(type(mantissaWidth) == "number", "argument #2 to BitBuffer.writeFloat should be a number") + assert( + mantissaWidth >= 1 and mantissaWidth <= 64, + "argument #2 to BitBuffer.writeFloat should be in the range [1, 64]" + ) + assert(mantissaWidth % 1 == 0, "argument #2 to BitBuffer.writeFloat should be an integer") + + assert(type(n) == "number", "argument #3 to BitBuffer.writeFloat should be a number") + + -- Given that floating point numbers are particularly hard to grasp, this function is annotated heavily. + -- This stackoverflow answer is a great help if you just want an overview: + -- https://stackoverflow.com/a/7645264 + -- Essentially, floating point numbers are scientific notation in binary. + -- Instead of expressing numbers like 10^e*m, floating points instead use 2^e*m. + -- For the sake of this function, `e` is referred to as `exponent` and `m` is referred to as `mantissa`. + + -- Floating point numbers are stored in memory as a sequence of bitfields. + -- Every float has a set number of bits assigned for exponent values and mantissa values, along with one bit for the sign. + -- The order of the bits in the memory is: sign, exponent, mantissa. + + -- Given that floating points have to represent numbers less than zero as well as those above them, + -- some parts of the exponent are set aside to be negative exponents. In the case of floats, + -- this is about half of the values. To calculate the 'real' value of an exponent a number that's half of the max exponent + -- is added to the exponent. More info can be found here: https://stackoverflow.com/q/2835278 + -- This number is called the 'bias'. + local bias = powers_of_2[exponentWidth - 1] - 1 + + local sign = n < 0 -- The sign of a number is important. + -- In this case, since we're using a lookup table for the sign bit, we want `sign` to indicate if the number is negative or not. + n = math.abs(n) -- But it's annoying to work with negative numbers and the sign isn't important for decomposition. + + -- Lua has a function specifically for decomposing (or taking apart) a floating point number into its pieces. + -- These pieces, as listed above, are the mantissa and exponent. + local mantissa, exponent = math.frexp(n) + + -- Before we go further, there are some concepts that get special treatment in the floating point format. + -- These have to be accounted for before normal floats are written to the buffer. + + if n == math.huge then + -- Positive and negative infinities are specifically indicated with an exponent that's all 1s + -- and a mantissa that's all 0s. + writeBits(BOOL_TO_BIT[sign]) -- As previously said, there's a bit for the sign + writeUnsigned(exponentWidth, powers_of_2[exponentWidth] - 1) -- Then comes the exponent + writeUnsigned(mantissaWidth, 0) -- And finally the mantissa + return + elseif n ~= n then + -- NaN is indicated with an exponent that's all 1s and a mantissa that isn't 0. + -- In theory, the individual bits of NaN should be maintained but Lua doesn't allow that, + -- so the mantissa is just being set to 10 for no particular reason. + writeBits(BOOL_TO_BIT[sign]) + writeUnsigned(exponentWidth, powers_of_2[exponentWidth] - 1) + writeUnsigned(mantissaWidth, 10) + return + elseif n == 0 then + -- Zero is represented with an exponent that's zero and a mantissa that's also zero. + -- Lua doesn't have a signed zero, so that translates to the entire number being all 0s. + writeUnsigned(exponentWidth + mantissaWidth + 1, 0) + return + elseif exponent + bias <= 1 then + -- Subnormal numbers are a number that's exponent (when biased) is zero. + -- Because of a quirk with the way Lua and C decompose numbers, subnormal numbers actually have an exponent of one when biased. + + -- The process behind this is explained below, so for the sake of brevity it isn't explained here. + -- The only difference between processing subnormal and normal numbers is with the mantissa. + -- As subnormal numbers always start with a 0 (in binary), it doesn't need to be removed or shifted out + -- so it's a simple shift and round. + mantissa = math.floor(mantissa * powers_of_2[mantissaWidth] + 0.5) + + writeBits(BOOL_TO_BIT[sign]) + writeUnsigned(exponentWidth, 0) -- Subnormal numbers always have zero for an exponent + writeUnsigned(mantissaWidth, mantissa) + return + end + + -- In every normal case, the mantissa of a number will have a 1 directly after the decimal point (in binary). + -- As an example, 0.15625 has a mantissa of 0.625, which is 0.101 in binary. The 1 after the decimal point is always there. + -- That means that for the sake of space efficiency that can be left out. + -- The bit has to be removed. This uses subtraction and multiplication to do it since bit32 is for integers only. + -- The mantissa is then shifted up by the width of the mantissa field and rounded. + mantissa = math.floor((mantissa - 0.5) * 2 * powers_of_2[mantissaWidth] + 0.5) + -- (The first fraction bit is equivalent to 0.5 in decimal) + + -- After that, it's just a matter of writing to the stream: + writeBits(BOOL_TO_BIT[sign]) + writeUnsigned(exponentWidth, exponent + bias - 1) -- The bias is added to the exponent to properly offset it + -- The extra -1 is added because Lua, for whatever reason, doesn't normalize its results + -- This is the cause of the 'quirk' mentioned when handling subnormal number + -- As an example, math.frexp(0.15625) = 0.625, -2 + -- This means that 0.15625 = 0.625*2^-2 + -- Or, in binary: 0.00101 = 0.101 >> 2 + -- This is a correct statement but the actual result is meant to be: + -- 0.00101 = 1.01 >> 3, or 0.15625 = 1.25*2^-3 + -- A small but important distinction that has made writing this module frustrating because no documentation notates this. + writeUnsigned(mantissaWidth, mantissa) + end + + local function writeBase64(input) + assert(type(input) == "string", "argument #1 to BitBuffer.writeBase64 should be a string") + assert( + not string.find(input, "[^%w%+/=]"), + "argument #1 to BitBuffer.writeBase64 should only contain valid base64 characters" + ) + + for i = 1, #input, 4 do + local b1, b2, b3, b4 = string.byte(input, i, i + 3) + + b1 = decode_char_set[b1] + b2 = decode_char_set[b2] + b3 = decode_char_set[b3] + b4 = decode_char_set[b4] + + local packed = bit32.bor(bit32.lshift(b1, 18), bit32.lshift(b2, 12), bit32.lshift(b3 or 0, 6), b4 or 0) + + writeByte(bit32.rshift(packed, 16)) + if not b3 then + break + end + writeByte(bit32.band(bit32.rshift(packed, 8), 0xff)) + if not b4 then + break + end + writeByte(bit32.band(packed, 0xff)) + end + end + + local function writeString(str) + assert(type(str) == "string", "argument #1 to BitBuffer.writeString should be a string") + -- The default mode of writing strings is length-prefixed. + -- This means that the length of the string is written before the contents of the string. + -- For the sake of speed it has to be an even byte. + -- One and two bytes is too few characters (255 bytes and 65535 bytes respectively), so it has to be higher. + -- Three bytes is roughly 16.77mb, and four is roughly 4.295gb. Given this is Lua and is thus unlikely to be processing strings + -- that large, this function uses three bytes, or 24 bits for the length + + writeUnsigned(24, #str) + + for i = 1, #str do + writeByte(string.byte(str, i, i)) + end + end + + local function writeTerminatedString(str) + assert(type(str) == "string", "argument #1 to BitBuffer.writeTerminatedString should be a string") + -- This function writes strings that are null-terminated. + -- Null-terminated strings are strings of bytes that end in a 0 byte (\0) + -- This isn't the default because it doesn't allow for binary data to be written cleanly. + + for i = 1, #str do + writeByte(string.byte(str, i, i)) + end + writeByte(0) + end + + local function writeSetLengthString(str) + assert(type(str) == "string", "argument #1 to BitBuffer.writeSetLengthString should be a string") + -- This function writes strings as a pure string of bytes + -- It doesn't store any data about the length of the string, + -- so reading it requires knowledge of how many characters were stored + + for i = 1, #str do + writeByte(string.byte(str, i, i)) + end + end + + local function writeField(...) + -- This is equivalent to having a writeBitfield function. + -- It combines all of the passed 'bits' into an unsigned number, then writes it. + local field = 0 + local bools = table.pack(...) + for i = 1, bools.n do + field = field * 2 -- Shift `field`. Equivalent to field<<1. At the beginning of the loop to avoid an extra shift. + + local v = bools[i] + if v then + field = field + 1 -- If the bit is truthy, turn it on (it defaults to off so it's fine to not have a branch) + end + end + + writeUnsigned(bools.n, field) + end + + -- All write functions below here are shorthands. For the sake of performance, these functions are implemented manually. + -- As an example, while it would certainly be easier to make `writeInt16(n)` just call `writeUnsigned(16, n), + -- it's more performant to just manually call writeByte twice for it. + + local function writeUInt8(n) + assert(type(n) == "number", "argument #1 to BitBuffer.writeUInt8 should be a number") + assert(n >= 0 and n <= 255, "argument #1 to BitBuffer.writeUInt8 should be in the range [0, 255]") + assert(n % 1 == 0, "argument #1 to BitBuffer.writeUInt8 should be an integer") + + writeByte(n) + end + + local function writeUInt16(n) + assert(type(n) == "number", "argument #1 to BitBuffer.writeUInt16 should be a number") + assert(n >= 0 and n <= 65535, "argument #1 to BitBuffer.writeInt16 should be in the range [0, 65535]") + assert(n % 1 == 0, "argument #1 to BitBuffer.writeUInt16 should be an integer") + + writeByte(bit32.rshift(n, 8)) + writeByte(bit32.band(n, 255)) + end + + local function writeUInt32(n) + assert(type(n) == "number", "argument #1 to BitBuffer.writeUInt32 should be a number") + assert( + n >= 0 and n <= 4294967295, + "argument #1 to BitBuffer.writeUInt32 should be in the range [0, 4294967295]" + ) + assert(n % 1 == 0, "argument #1 to BitBuffer.writeUInt32 should be an integer") + + writeByte(bit32.rshift(n, 24)) + writeByte(bit32.band(bit32.rshift(n, 16), 255)) + writeByte(bit32.band(bit32.rshift(n, 8), 255)) + writeByte(bit32.band(n, 255)) + end + + local function writeInt8(n) + assert(type(n) == "number", "argument #1 to BitBuffer.writeInt8 should be a number") + assert(n >= -128 and n <= 127, "argument #1 to BitBuffer.writeInt8 should be in the range [-128, 127]") + assert(n % 1 == 0, "argument #1 to BitBuffer.writeInt8 should be an integer") + + if n < 0 then + n = (128 + n) + 128 + end + + writeByte(n) + end + + local function writeInt16(n) + assert(type(n) == "number", "argument #1 to BitBuffer.writeInt16 should be a number") + assert(n >= -32768 and n <= 32767, "argument #1 to BitBuffer.writeInt16 should be in the range [-32768, 32767]") + assert(n % 1 == 0, "argument #1 to BitBuffer.writeInt16 should be an integer") + + if n < 0 then + n = (32768 + n) + 32768 + end + + writeByte(bit32.rshift(n, 8)) + writeByte(bit32.band(n, 255)) + end + + local function writeInt32(n) + assert(type(n) == "number", "argument #1 to BitBuffer.writeInt32 should be a number") + assert( + n >= -2147483648 and n <= 2147483647, + "argument #1 to BitBuffer.writeInt32 should be in the range [-2147483648, 2147483647]" + ) + assert(n % 1 == 0, "argument #1 to BitBuffer.writeInt32 should be an integer") + + if n < 0 then + n = (2147483648 + n) + 2147483648 + end + + writeByte(bit32.rshift(n, 24)) + writeByte(bit32.band(bit32.rshift(n, 16), 255)) + writeByte(bit32.band(bit32.rshift(n, 8), 255)) + writeByte(bit32.band(n, 255)) + end + + local function writeFloat16(n) + --assert(type(n) == "number", "argument #1 to BitBuffer.writeFloat16 should be a number") + + local sign = n < 0 + n = math.abs(n) + + local mantissa, exponent = math.frexp(n) + + if n == math.huge then + if sign then + writeByte(252) -- 11111100 + else + writeByte(124) -- 01111100 + end + writeByte(0) -- 00000000 + return + elseif n ~= n then + -- 01111111 11111111 + writeByte(127) + writeByte(255) + return + elseif n == 0 then + writeByte(0) + writeByte(0) + return + elseif exponent + 15 <= 1 then -- Bias for halfs is 15 + mantissa = math.floor(mantissa * 1024 + 0.5) + if sign then + writeByte(128 + bit32.rshift(mantissa, 8)) -- Sign bit, 5 empty bits, 2 from mantissa + else + writeByte(bit32.rshift(mantissa, 8)) + end + writeByte(bit32.band(mantissa, 255)) -- Get last 8 bits from mantissa + return + end + + mantissa = math.floor((mantissa - 0.5) * 2048 + 0.5) + + -- The bias for halfs is 15, 15-1 is 14 + if sign then + writeByte(128 + bit32.lshift(exponent + 14, 2) + bit32.rshift(mantissa, 8)) + else + writeByte(bit32.lshift(exponent + 14, 2) + bit32.rshift(mantissa, 8)) + end + writeByte(bit32.band(mantissa, 255)) + end + + local function writeFloat32(n) + --assert(type(n) == "number", "argument #1 to BitBuffer.writeFloat32 should be a number") + + local sign = n < 0 + n = math.abs(n) + + local mantissa, exponent = math.frexp(n) + + if n == math.huge then + if sign then + writeByte(255) -- 11111111 + else + writeByte(127) -- 01111111 + end + writeByte(128) -- 10000000 + writeByte(0) -- 00000000 + writeByte(0) -- 00000000 + return + elseif n ~= n then + -- 01111111 11111111 11111111 11111111 + writeByte(127) + writeByte(255) + writeByte(255) + writeByte(255) + return + elseif n == 0 then + writeByte(0) + writeByte(0) + writeByte(0) + writeByte(0) + return + elseif exponent + 127 <= 1 then -- bias for singles is 127 + mantissa = math.floor(mantissa * 8388608 + 0.5) + if sign then + writeByte(128) -- Sign bit, 7 empty bits for exponent + else + writeByte(0) + end + writeByte(bit32.rshift(mantissa, 16)) + writeByte(bit32.band(bit32.rshift(mantissa, 8), 255)) + writeByte(bit32.band(mantissa, 255)) + return + end + + mantissa = math.floor((mantissa - 0.5) * 16777216 + 0.5) + + -- 127-1 = 126 + if sign then -- sign + 7 exponent + writeByte(128 + bit32.rshift(exponent + 126, 1)) + else + writeByte(bit32.rshift(exponent + 126, 1)) + end + writeByte(bit32.band(bit32.lshift(exponent + 126, 7), 255) + bit32.rshift(mantissa, 16)) -- 1 exponent + 7 mantissa + writeByte(bit32.band(bit32.rshift(mantissa, 8), 255)) -- 8 mantissa + writeByte(bit32.band(mantissa, 255)) -- 8 mantissa + end + + local function writeFloat64(n) + assert(type(n) == "number", "argument #1 to BitBuffer.writeFloat64 should be a number") + + local sign = n < 0 + n = math.abs(n) + + local mantissa, exponent = math.frexp(n) + + if n == math.huge then + if sign then + writeByte(255) -- 11111111 + else + writeByte(127) -- 01111111 + end + writeByte(240) -- 11110000 + writeByte(0) -- 00000000 + writeByte(0) -- 00000000 + writeByte(0) -- 00000000 + writeByte(0) -- 00000000 + writeByte(0) -- 00000000 + writeByte(0) -- 00000000 + return + elseif n ~= n then + -- 01111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 + writeByte(127) + writeByte(255) + writeByte(255) + writeByte(255) + writeByte(255) + writeByte(255) + writeByte(255) + writeByte(255) + return + elseif n == 0 then + writeByte(0) + return + elseif exponent + 1023 <= 1 then -- bias for doubles is 1023 + mantissa = math.floor(mantissa * 4503599627370496 + 0.5) + if sign then + writeByte(128) -- Sign bit, 7 empty bits for exponent + else + writeByte(0) + end + + -- This is labeled better below + local leastSignificantChunk = mantissa % 0x100000000 -- 32 bits + local mostSignificantChunk = math.floor(mantissa / 0x100000000) -- 20 bits + + writeByte(bit32.rshift(mostSignificantChunk, 16)) + writeByte(bit32.band(bit32.rshift(mostSignificantChunk, 8), 255)) + writeByte(bit32.band(mostSignificantChunk, 255)) + writeByte(bit32.rshift(leastSignificantChunk, 24)) + writeByte(bit32.band(bit32.rshift(leastSignificantChunk, 16), 255)) + writeByte(bit32.band(bit32.rshift(leastSignificantChunk, 8), 255)) + writeByte(bit32.band(leastSignificantChunk, 255)) + return + end + + mantissa = math.floor((mantissa - 0.5) * 9007199254740992 + 0.5) + + --1023-1 = 1022 + if sign then + writeByte(128 + bit32.rshift(exponent + 1022, 4)) -- shift out 4 of the bits in exponent + else + writeByte(bit32.rshift(exponent + 1022, 4)) -- 01000001 0110 + end + -- Things start to get a bit wack here because the mantissa is 52 bits, so bit32 *can't* be used. + -- As the Offspring once said... You gotta keep 'em seperated. + local leastSignificantChunk = mantissa % 0x100000000 -- 32 bits + local mostSignificantChunk = math.floor(mantissa / 0x100000000) -- 20 bits + + -- First, the last 4 bits of the exponent and the first 4 bits of the mostSignificantChunk: + writeByte(bit32.band(bit32.lshift(exponent + 1022, 4), 255) + bit32.rshift(mostSignificantChunk, 16)) + -- Then, the next 16 bits: + writeByte(bit32.band(bit32.rshift(mostSignificantChunk, 8), 255)) + writeByte(bit32.band(mostSignificantChunk, 255)) + -- Then... 4 bytes of the leastSignificantChunk + writeByte(bit32.rshift(leastSignificantChunk, 24)) + writeByte(bit32.band(bit32.rshift(leastSignificantChunk, 16), 255)) + writeByte(bit32.band(bit32.rshift(leastSignificantChunk, 8), 255)) + writeByte(bit32.band(leastSignificantChunk, 255)) + end + + -- All write functions below here are Roblox specific datatypes. + + local function writeBrickColor(n) + assert(typeof(n) == "BrickColor", "argument #1 to BitBuffer.writeBrickColor should be a BrickColor") + + writeUInt16(n.Number) + end + + local function writeColor3(c3) + assert(typeof(c3) == "Color3", "argument #1 to BitBuffer.writeColor3 should be a Color3") + + writeByte(math.floor(c3.R * 0xff + 0.5)) + writeByte(math.floor(c3.G * 0xff + 0.5)) + writeByte(math.floor(c3.B * 0xff + 0.5)) + end + + local function writeCFrame(cf) + assert(typeof(cf) == "CFrame", "argument #1 to BitBuffer.writeCFrame should be a CFrame") + -- CFrames can be rather lengthy (if stored naively, they would each be 48 bytes long) so some optimization is done here. + -- Specifically, if a CFrame is axis-aligned (it's only rotated in 90 degree increments), the rotation matrix isn't stored. + -- Instead, an 'id' for its orientation is generated and that's stored instead of the rotation. + -- This means that for the most common rotations, only 13 bytes are used. + -- The downside is that non-axis-aligned CFrames use 49 bytes instead of 48, but that's a small price to pay. + + local upVector = cf.UpVector + local rightVector = cf.RightVector + + -- This is an easy trick to check if a CFrame is axis-aligned: + -- Essentially, in order for a vector to be axis-aligned, two of the components have to be 0 + -- This means that the dot product between the vector and a vector of all 1s will be 1 (0*x = 0) + -- Since these are all unit vectors, there is no other combination that results in 1. + local rightAligned = math.abs(rightVector:Dot(ONES_VECTOR)) + local upAligned = math.abs(upVector:Dot(ONES_VECTOR)) + -- At least one of these two vectors is guaranteed to not result in 0. + + local axisAligned = (math.abs(1 - rightAligned) < 0.00001 or rightAligned == 0) + and (math.abs(1 - upAligned) < 0.00001 or upAligned == 0) + -- There are limitations to `math.abs(a-b) < epsilon` but they're not relevant: + -- The range of numbers is [0, 1] and this just needs to know if the number is approximately 1 + + --todo special code for quaternions (0x01 in Roblox's format, would clash with 0x00 here) + if axisAligned then + local position = cf.Position + -- The ID of an orientation is generated through what can best be described as 'hand waving'; + -- This is how Roblox does it and it works, so it was chosen to do it this way too. + local rightNormal, upNormal + for i = 0, 5 do + local v = NORMAL_ID_VECTORS[i] + if 1 - v:Dot(rightVector) < 0.00001 then + rightNormal = i + end + if 1 - v:Dot(upVector) < 0.00001 then + upNormal = i + end + end + -- The ID generated here is technically off by 1 from what Roblox would store, but that's not important + -- It just means that 0x02 is actually 0x01 for the purposes of this module's implementation. + writeByte(rightNormal * 6 + upNormal) + writeFloat32(position.X) + writeFloat32(position.Y) + writeFloat32(position.Z) + else + -- If the CFrame isn't axis-aligned, the entire rotation matrix has to be written... + writeByte(0) -- Along with a byte to indicate the matrix was written. + local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = cf:GetComponents() + writeFloat32(x) + writeFloat32(y) + writeFloat32(z) + writeFloat32(r00) + writeFloat32(r01) + writeFloat32(r02) + writeFloat32(r10) + writeFloat32(r11) + writeFloat32(r12) + writeFloat32(r20) + writeFloat32(r21) + writeFloat32(r22) + end + end + + local function writeVector3(v3) + --assert(typeof(v3) == "Vector3", "argument #1 to BitBuffer.writeVector3 should be a Vector3") + + writeFloat32(v3.X) + writeFloat32(v3.Y) + writeFloat32(v3.Z) + end + + local function writeVector2(v2) + assert(typeof(v2) == "Vector2", "argument #1 to BitBuffer.writeVector2 should be a Vector2") + + writeFloat32(v2.X) + writeFloat32(v2.Y) + end + + local function writeUDim2(u2) + assert(typeof(u2) == "UDim2", "argument #1 to BitBuffer.writeUDim2 should be a UDim2") + + writeFloat32(u2.X.Scale) + writeInt32(u2.X.Offset) + writeFloat32(u2.Y.Scale) + writeInt32(u2.Y.Offset) + end + + local function writeUDim(u) + assert(typeof(u) == "UDim", "argument #1 to BitBuffer.writeUDim should be a UDim") + + writeFloat32(u.Scale) + writeInt32(u.Offset) + end + + local function writeRay(ray) + assert(typeof(ray) == "Ray", "argument #1 to BitBuffer.writeRay should be a Ray") + + writeFloat32(ray.Origin.X) + writeFloat32(ray.Origin.Y) + writeFloat32(ray.Origin.Z) + + writeFloat32(ray.Direction.X) + writeFloat32(ray.Direction.Y) + writeFloat32(ray.Direction.Z) + end + + local function writeRect(rect) + assert(typeof(rect) == "Rect", "argument #1 to BitBuffer.writeRect should be a Rect") + + writeFloat32(rect.Min.X) + writeFloat32(rect.Min.Y) + + writeFloat32(rect.Max.X) + writeFloat32(rect.Max.Y) + end + + local function writeRegion3(region) + assert(typeof(region) == "Region3", "argument #1 to BitBuffer.writeRegion3 should be a Region3") + + local min = region.CFrame.Position - (region.Size / 2) + local max = region.CFrame.Position + (region.Size / 2) + + writeFloat32(min.X) + writeFloat32(min.Y) + writeFloat32(min.Z) + + writeFloat32(max.X) + writeFloat32(max.Y) + writeFloat32(max.Z) + end + + local function writeEnum(enum) + assert(typeof(enum) == "EnumItem", "argument #1 to BitBuffer.writeEnum should be an EnumItem") + + -- Relying upon tostring is generally not good, but there's not any other options for this. + writeTerminatedString(tostring(enum.EnumType)) + writeUInt16(enum.Value) -- Optimistically assuming no Roblox Enum value will ever pass 65,535 + end + + local function writeNumberRange(range) + assert(typeof(range) == "NumberRange", "argument #1 to BitBuffer.writeNumberRange should be a NumberRange") + + writeFloat32(range.Min) + writeFloat32(range.Max) + end + + local function writeNumberSequence(sequence) + assert( + typeof(sequence) == "NumberSequence", + "argument #1 to BitBuffer.writeNumberSequence should be a NumberSequence" + ) + + writeUInt32(#sequence.Keypoints) + for _, keypoint in ipairs(sequence.Keypoints) do + writeFloat32(keypoint.Time) + writeFloat32(keypoint.Value) + writeFloat32(keypoint.Envelope) + end + end + + local function writeColorSequence(sequence) + assert( + typeof(sequence) == "ColorSequence", + "argument #1 to BitBuffer.writeColorSequence should be a ColorSequence" + ) + + writeUInt32(#sequence.Keypoints) + for _, keypoint in ipairs(sequence.Keypoints) do + local c3 = keypoint.Value + writeFloat32(keypoint.Time) + writeByte(math.floor(c3.R * 0xff + 0.5)) + writeByte(math.floor(c3.G * 0xff + 0.5)) + writeByte(math.floor(c3.B * 0xff + 0.5)) + end + end + + -- These are the read functions for the 'abstract' data types. At the bottom, there are shorthand read functions. + + local function readBits(n) + assert(type(n) == "number", "argument #1 to BitBuffer.readBits should be a number") + assert(n > 0, "argument #1 to BitBuffer.readBits should be greater than zero") + assert(n % 1 == 0, "argument #1 to BitBuffer.readBits should be an integer") + + assert(pointer + n <= bitCount, "BitBuffer.readBits cannot read past the end of the stream") + + -- The first of two main functions for the actual 'reading' of the bitbuffer. + -- Reads `n` bits and returns an array of their values. + local output = table.create(n) --! + local byte = bytes[pointerByte] -- For the sake of efficiency, the current byte that the bits are coming from is stored + local c = pointer % 8 -- A counter is set with the current position of the pointer in the byte + for i = 1, n do + -- Then, it's as easy as moving through the bits of the byte + -- And getting the individiual bit values + local pow = powers_of_2[7 - c] + output[i] = BOOL_TO_BIT[bit32.btest(byte, pow)] -- Test if a bit is on by &ing it by 2^[bit position] + c = c + 1 + if c == 8 then -- If the byte boundary is reached, increment pointerByte and store the new byte in `byte` + pointerByte = pointerByte + 1 + byte = bytes[pointerByte] + c = 0 + end + end + pointer = pointer + n -- Move the pointer forward + return output + end + + --Skip to the end of the current byte + local function skipStrayBits() + local c = pointer % 8 + if (c > 0) then + pointer += 8 - c + pointerByte += 1 + end + end + + local function readBytesFast() + return bytes + end + + + local function readByte() + assert(pointer + 8 <= bitCount, "BitBuffer.readByte cannot read past the end of the stream") + -- The second of two main functions for the actual 'reading' of the bitbuffer. + -- Reads a byte and returns it + local c = pointer % 8 -- How far into the pointerByte the pointer is + local byte1 = bytes[pointerByte] -- The pointerByte + pointer = pointer + 8 + if c == 0 then -- Trivial if the pointer is at the beginning of the pointerByte + pointerByte = pointerByte + 1 + return byte1 + else + pointerByte = pointerByte + 1 + -- Get the remainder of the first pointerByte and add it to the part of the new pointerByte that's required + -- Both these methods are explained in writeByte + return bit32.band(bit32.lshift(byte1, c), 255) + bit32.rshift(bytes[pointerByte], 8 - c) + end + end + + local function readUnsigned(width) + assert(type(width) == "number", "argument #1 to BitBuffer.readUnsigned should be a number") + assert(width >= 1 and width <= 64, "argument #1 to BitBuffer.readUnsigned should be in the range [1, 64]") + assert(width % 1 == 0, "argument #1 to BitBuffer.readUnsigned should be an integer") + + assert(pointer + width <= bitCount, "BitBuffer.readUnsigned cannot read past the end of the stream") + -- Implementing this on its own was considered because of a worry that it would be inefficient to call + -- readByte and readBit several times, but it was decided the simplicity is worth a minor performance hit. + local bytesInN, bitsInN = math.floor(width / 8), width % 8 + + -- No check is required for if the width is greater than 32 because bit32 isn't used. + local n = 0 + -- Shift and add a read byte however many times is necessary + -- Adding after shifting is importnat - it prevents there from being 8 empty bits of space + for _ = 1, bytesInN do + n = n * 0x100 -- 2^8; equivalent to n << 8 + n = n + readByte() + end + -- The bits are then read and added to the number + if bitsInN ~= 0 then + for _, v in ipairs(readBits(width % 8)) do --todo benchmark against concat+tonumber; might be worth the code smell + n = n * 2 + n = n + v + end + end + return n + end + + local function readSigned(width) + assert(type(width) == "number", "argument #1 to BitBuffer.readSigned should be a number") + assert(width >= 2 and width <= 64, "argument #1 to BitBuffer.readSigned should be in the range [2, 64]") + assert(width % 1 == 0, "argument #1 to BitBuffer.readSigned should be an integer") + + assert(pointer + 8 <= bitCount, "BitBuffer.readSigned cannot read past the end of the stream") + local sign = readBits(1)[1] + local n = readUnsigned(width - 1) -- Again, width-1 is because one bit is used for the sign + + -- As said in writeSigned, the written number is unmodified if the number is positive (the sign bit is 0) + if sign == 0 then + return n + else + -- And the number is equal to max value of the width + the number if the number is negative (the sign bit is 1) + -- To reverse that, the max value is subtracted from the stored number. + return n - powers_of_2[width - 1] + end + end + + local function readFloat(exponentWidth, mantissaWidth) + assert(type(exponentWidth) == "number", "argument #1 to BitBuffer.readFloat should be a number") + assert( + exponentWidth >= 1 and exponentWidth <= 64, + "argument #1 to BitBuffer.readFloat should be in the range [1, 64]" + ) + assert(exponentWidth % 1 == 0, "argument #1 to BitBuffer.readFloat should be an integer") + + assert(type(mantissaWidth) == "number", "argument #2 to BitBuffer.readFloat should be a number") + assert( + mantissaWidth >= 1 and mantissaWidth <= 64, + "argument #2 to BitBuffer.readFloat should be in the range [1, 64]" + ) + assert(mantissaWidth % 1 == 0, "argument #2 to BitBuffer.readFloat should be an integer") + + assert( + pointer + exponentWidth + mantissaWidth + 1 <= bitCount, + "BitBuffer.readFloat cannot read past the end of the stream" + ) + -- Recomposing floats is rather straightfoward. + -- The bias is subtracted from the exponent, the mantissa is shifted back by mantissaWidth, one is added to the mantissa + -- and the whole thing is recomposed with math.ldexp (this is identical to mantissa*(2^exponent)). + + local bias = powers_of_2[exponentWidth - 1] - 1 + + local sign = readBits(1)[1] + local exponent = readUnsigned(exponentWidth) + local mantissa = readUnsigned(mantissaWidth) + + -- Before normal numbers are handled though, special cases and subnormal numbers are once again handled seperately + if exponent == powers_of_2[exponentWidth] - 1 then + if mantissa ~= 0 then -- If the exponent is all 1s and the mantissa isn't zero, the number is NaN + return 0 / 0 + else -- Otherwise, it's positive or negative infinity + return sign == 0 and math.huge or -math.huge + end + elseif exponent == 0 then + if mantissa == 0 then -- If the exponent and mantissa are both zero, the number is zero. + return 0 + else -- If the exponent is zero and the mantissa is not zero, the number is subnormal + -- Subnormal numbers are straightforward: shifting the mantissa so that it's a fraction is all that's required + mantissa = mantissa / powers_of_2[mantissaWidth] + + -- Since the exponent is 0, it's actual value is just -bias (it would be exponent-bias) + -- As previously touched on in writeFloat, the exponent value is off by 1 in Lua though. + return sign == 1 and -math.ldexp(mantissa, -bias + 1) or math.ldexp(mantissa, -bias + 1) + end + end + + -- First, the mantissa is shifted back by the mantissaWidth + -- Then, 1 is added to it to 'normalize' it. + mantissa = (mantissa / powers_of_2[mantissaWidth]) + 1 + + -- Because the mantissa is normalized above (the leading 1 is in the ones place), it's accurate to say exponent-bias + return sign == 1 and -math.ldexp(mantissa, exponent - bias) or math.ldexp(mantissa, exponent - bias) + end + + local function readString() + assert(pointer + 24 <= bitCount, "BitBuffer.readString cannot read past the end of the stream") + -- Reading a length-prefixed string is rather straight forward. + -- The length is read, then that many bytes are read and put in a string. + + local stringLength = readUnsigned(24) + assert(pointer + (stringLength * 8) <= bitCount, "BitBuffer.readString cannot read past the end of the stream") + + local outputCharacters = table.create(stringLength) --! + + for i = 1, stringLength do + outputCharacters[i] = readByte() + end + + local output = table.create(math.ceil(stringLength / 4096)) + local k = 1 + for i = 1, stringLength, 4096 do + output[k] = string.char(table.unpack(outputCharacters, i, math.min(stringLength, i + 4095))) + k = k + 1 + end + + return table.concat(output) + end + + local function readTerminatedString() + local outputCharacters = {} + + -- Bytes are read continuously until either a nul-character is reached or until the stream runs out. + local length = 0 + while true do + local byte = readByte() + if not byte then -- Stream has ended + error("BitBuffer.readTerminatedString cannot read past the end of the stream", 2) + elseif byte == 0 then -- String has ended + break + else -- Add byte to string + length = length + 1 + outputCharacters[length] = byte + end + end + + local output = table.create(math.ceil(length / 4096)) + local k = 1 + for l = 1, length, 4096 do + output[k] = string.char(table.unpack(outputCharacters, l, math.min(length, l + 4095))) + k = k + 1 + end + + return table.concat(output) + end + + local function readSetLengthString(length) + assert(type(length) == "number", "argument #1 to BitBuffer.readSetLengthString should be a number") + assert(length >= 0, "argument #1 to BitBuffer.readSetLengthString should be zero or higher.") + assert(length % 1 == 0, "argument #1 to BitBuffer.readSetLengthString should be an integer") + + assert( + pointer + (length * 8) <= bitCount, + "BitBuffer.readSetLengthString cannot read past the end of the stream" + ) + -- `length` number of bytes are read and put into a string + + local outputCharacters = table.create(length) --! + + for i = 1, length do + outputCharacters[i] = readByte() + end + + local output = table.create(math.ceil(length / 4096)) + local k = 1 + for i = 1, length, 4096 do + output[k] = string.char(table.unpack(outputCharacters, i, math.min(length, i + 4095))) + k = k + 1 + end + + return table.concat(output) + end + + local function readField(n) + assert(type(n) == "number", "argument #1 to BitBuffer.readField should be a number") + assert(n > 0, "argument #1 to BitBuffer.readField should be above 0") + assert(n % 1 == 0, "argument #1 to BitBuffer.readField should be an integer") + + assert(pointer + n <= bitCount, "BitBuffer.readField cannot read past the end of the stream") + -- Reading a bit field is again rather simple. You read the actual field, then take the bits out. + local readInt = readUnsigned(n) + local output = table.create(n) --! + + for i = n, 1, -1 do -- In reverse order since we're pulling bits out from lsb to msb + output[i] = readInt % 2 == 1 -- Equivalent to an extraction of the lsb + readInt = math.floor(readInt / 2) -- Equivalent to readInt>>1 + end + + return output + end + + -- All read functions below here are shorthands. + -- As with their write variants, these functions are implemented manually using readByte for performance reasons. + + local function readUInt8() + assert(pointer + 8 <= bitCount, "BitBuffer.readUInt8 cannot read past the end of the stream") + + return readByte() + end + + local function readUInt16() + assert(pointer + 16 <= bitCount, "BitBuffer.readUInt16 cannot read past the end of the stream") + + return bit32.lshift(readByte(), 8) + readByte() + end + + local function readUInt32() + assert(pointer + 32 <= bitCount, "BitBuffer.readUInt32 cannot read past the end of the stream") + + return bit32.lshift(readByte(), 24) + bit32.lshift(readByte(), 16) + bit32.lshift(readByte(), 8) + readByte() + end + + local function readInt8() + assert(pointer + 8 <= bitCount, "BitBuffer.readInt8 cannot read past the end of the stream") + + local n = readByte() + local sign = bit32.btest(n, 128) + n = bit32.band(n, 127) + + if sign then + return n - 128 + else + return n + end + end + + local function readInt16() + assert(pointer + 16 <= bitCount, "BitBuffer.readInt16 cannot read past the end of the stream") + + local n = bit32.lshift(readByte(), 8) + readByte() + local sign = bit32.btest(n, 32768) + n = bit32.band(n, 32767) + + if sign then + return n - 32768 + else + return n + end + end + + local function readInt32() + assert(pointer + 32 <= bitCount, "BitBuffer.readInt32 cannot read past the end of the stream") + + local n = bit32.lshift(readByte(), 24) + bit32.lshift(readByte(), 16) + bit32.lshift(readByte(), 8) + readByte() + local sign = bit32.btest(n, 2147483648) + n = bit32.band(n, 2147483647) + + if sign then + return n - 2147483648 + else + return n + end + end + + local function readFloat16() + assert(pointer + 16 <= bitCount, "BitBuffer.readFloat16 cannot read past the end of the stream") + + local b0 = readByte() + local sign = bit32.btest(b0, 128) + local exponent = bit32.rshift(bit32.band(b0, 127), 2) + local mantissa = bit32.lshift(bit32.band(b0, 3), 8) + readByte() + + if exponent == 31 then --2^5-1 + if mantissa ~= 0 then + return 0 / 0 + else + return sign and -math.huge or math.huge + end + elseif exponent == 0 then + if mantissa == 0 then + return 0 + else + return sign and -math.ldexp(mantissa / 1024, -14) or math.ldexp(mantissa / 1024, -14) + end + end + + mantissa = (mantissa / 1024) + 1 + + return sign and -math.ldexp(mantissa, exponent - 15) or math.ldexp(mantissa, exponent - 15) + end + + local function readFloat32() + assert(pointer + 32 <= bitCount, "BitBuffer.readFloat32 cannot read past the end of the stream") + + local b0 = readByte() + local b1 = readByte() + local sign = bit32.btest(b0, 128) + local exponent = bit32.band(bit32.lshift(b0, 1), 255) + bit32.rshift(b1, 7) + local mantissa = bit32.lshift(bit32.band(b1, 127), 23 - 7) + + bit32.lshift(readByte(), 23 - 7 - 8) + + bit32.lshift(readByte(), 23 - 7 - 8 - 8) + + if exponent == 255 then -- 2^8-1 + if mantissa ~= 0 then + return 0 / 0 + else + return sign and -math.huge or math.huge + end + elseif exponent == 0 then + if mantissa == 0 then + return 0 + else + -- -126 is the 0-bias+1 + return sign and -math.ldexp(mantissa / 8388608, -126) or math.ldexp(mantissa / 8388608, -126) + end + end + + mantissa = (mantissa / 8388608) + 1 + + return sign and -math.ldexp(mantissa, exponent - 127) or math.ldexp(mantissa, exponent - 127) + end + + local function readFloat64() + assert(pointer + 64 <= bitCount, "BitBuffer.readFloat64 cannot read past the end of the stream") + + local b0 = readByte() + local b1 = readByte() + + local sign = bit32.btest(b0, 128) + local exponent = bit32.lshift(bit32.band(b0, 127), 4) + bit32.rshift(b1, 4) + local mostSignificantChunk = bit32.lshift(bit32.band(b1, 15), 16) + bit32.lshift(readByte(), 8) + readByte() + local leastSignificantChunk = bit32.lshift(readByte(), 24) + + bit32.lshift(readByte(), 16) + + bit32.lshift(readByte(), 8) + + readByte() + + -- local mantissa = (bit32.lshift(bit32.band(b1, 15), 16)+bit32.lshift(readByte(), 8)+readByte())*0x100000000+ + -- bit32.lshift(readByte(), 24)+bit32.lshift(readByte(), 16)+bit32.lshift(readByte(), 8)+readByte() + + local mantissa = mostSignificantChunk * 0x100000000 + leastSignificantChunk + + if exponent == 2047 then -- 2^11-1 + if mantissa ~= 0 then + return 0 / 0 + else + return sign and -math.huge or math.huge + end + elseif exponent == 0 then + if mantissa == 0 then + return 0 + else + return sign and -math.ldexp(mantissa / 4503599627370496, -1022) + or math.ldexp(mantissa / 4503599627370496, -1022) + end + end + + mantissa = (mantissa / 4503599627370496) + 1 + + return sign and -math.ldexp(mantissa, exponent - 1023) or math.ldexp(mantissa, exponent - 1023) + end + + -- All read functions below here are Roblox specific datatypes. + + local function readBrickColor() + assert(pointer + 16 <= bitCount, "BitBuffer.readBrickColor cannot read past the end of the stream") + + return BrickColor.new(readUInt16()) + end + + local function readColor3() + assert(pointer + 24 <= bitCount, "BitBuffer.readColor3 cannot read past the end of the stream") + + return Color3.fromRGB(readByte(), readByte(), readByte()) + end + + local function readCFrame() + assert(pointer + 8 <= bitCount, "BitBuffer.readCFrame cannot read past the end of the stream") + + local id = readByte() + + if id == 0 then + assert(pointer + 384 <= bitCount, "BitBuffer.readCFrame cannot read past the end of the stream") -- 4*12 bytes = 383 bits + + -- stylua: ignore + return CFrame.new( + readFloat32(), readFloat32(), readFloat32(), + readFloat32(), readFloat32(), readFloat32(), + readFloat32(), readFloat32(), readFloat32(), + readFloat32(), readFloat32(), readFloat32() + ) + else + assert(pointer + 96 <= bitCount, "BitBuffer.readCFrame cannot read past the end of the stream") -- 4*3 bytes = 96 bits + + local rightVector = NORMAL_ID_VECTORS[math.floor(id / 6)] + local upVector = NORMAL_ID_VECTORS[id % 6] + local lookVector = rightVector:Cross(upVector) + + -- CFrame's full-matrix constructor takes right/up/look vectors as columns... + -- stylua: ignore + return CFrame.new( + readFloat32(), readFloat32(), readFloat32(), + rightVector.X, upVector.X, lookVector.X, + rightVector.Y, upVector.Y, lookVector.Y, + rightVector.Z, upVector.Z, lookVector.Z + ) + end + end + + local function readVector3() + assert(pointer + 96 <= bitCount, "BitBuffer.readVector3 cannot read past the end of the stream") + + return Vector3.new(readFloat32(), readFloat32(), readFloat32()) + end + + local function readVector2() + assert(pointer + 64 <= bitCount, "BitBuffer.readVector2 cannot read past the end of the stream") + + return Vector2.new(readFloat32(), readFloat32()) + end + + local function readUDim2() + assert(pointer + 128 <= bitCount, "BitBuffer.readUDim2 cannot read past the end of the stream") + + return UDim2.new(readFloat32(), readInt32(), readFloat32(), readInt32()) + end + + local function readUDim() + assert(pointer + 64 <= bitCount, "BitBuffer.readUDim cannot read past the end of the stream") + + return UDim.new(readFloat32(), readInt32()) + end + + local function readRay() + assert(pointer + 192 <= bitCount, "BitBuffer.readRay cannot read past the end of the stream") + + return Ray.new( + Vector3.new(readFloat32(), readFloat32(), readFloat32()), + Vector3.new(readFloat32(), readFloat32(), readFloat32()) + ) + end + + local function readRect() + assert(pointer + 128 <= bitCount, "BitBuffer.readRect cannot read past the end of the stream") + + return Rect.new(readFloat32(), readFloat32(), readFloat32(), readFloat32()) + end + + local function readRegion3() + assert(pointer + 192 <= bitCount, "BitBuffer.readRegion3 cannot read past the end of the stream") + + return Region3.new( + Vector3.new(readFloat32(), readFloat32(), readFloat32()), + Vector3.new(readFloat32(), readFloat32(), readFloat32()) + ) + end + + local function readEnum() + assert(pointer + 8 <= bitCount, "BitBuffer.readEnum cannot read past the end of the stream") + + local name = readTerminatedString() -- This might expose an error from readString to the end-user but it's not worth the hassle to fix. + + assert(pointer + 16 <= bitCount, "BitBuffer.readEnum cannot read past the end of the stream") + + local value = readUInt16() -- Again, optimistically assuming no Roblox Enum value will ever pass 65,535 + + -- Catching a potential error only to throw it with different formatting seems... Superfluous. + -- Open an issue on github if you feel otherwise. + for _, v in ipairs(Enum[name]:GetEnumItems()) do + if v.Value == value then + return v + end + end + + error( + "BitBuffer.readEnum could not get value: `" + .. tostring(value) + .. "` is not a valid member of `" + .. name + .. "`", + 2 + ) + end + + local function readNumberRange() + assert(pointer + 64 <= bitCount, "BitBuffer.readNumberRange cannot read past the end of the stream") + + return NumberRange.new(readFloat32(), readFloat32()) + end + + local function readNumberSequence() + assert(pointer + 32 <= bitCount, "BitBuffer.readNumberSequence cannot read past the end of the stream") + + local keypointCount = readUInt32() + + assert(pointer + keypointCount * 96, "BitBuffer.readColorSequence cannot read past the end of the stream") + + local keypoints = table.create(keypointCount) + + -- As it turns out, creating a NumberSequence with a negative value as its first argument (in the first and second constructor) + -- creates NumberSequenceKeypoints with negative envelopes. The envelope is read and saved properly, as you would expect, + -- but you can't create a NumberSequence with a negative envelope if you're using a table of keypoints (which is happening here). + -- If you're confused, run this snippet: NumberSequence.new(NumberSequence.new(-1).Keypoints) + -- As a result, there has to be some branching logic in this function. + -- ColorSequences don't have envelopes so it's not necessary for them. + + for i = 1, keypointCount do + local time, value, envelope = readFloat32(), readFloat32(), readFloat32() + if value < 0 then + envelope = nil + end + keypoints[i] = NumberSequenceKeypoint.new(time, value, envelope) + end + + return NumberSequence.new(keypoints) + end + + local function readColorSequence() + assert(pointer + 32 <= bitCount, "BitBuffer.readColorSequence cannot read past the end of the stream") + + local keypointCount = readUInt32() + + assert(pointer + keypointCount * 56, "BitBuffer.readColorSequence cannot read past the end of the stream") + + local keypoints = table.create(keypointCount) + + for i = 1, keypointCount do + keypoints[i] = ColorSequenceKeypoint.new(readFloat32(), Color3.fromRGB(readByte(), readByte(), readByte())) + end + + return ColorSequence.new(keypoints) + end + + return { + dumpBinary = dumpBinary, + dumpString = dumpString, + dumpHex = dumpHex, + dumpBase64 = dumpBase64, + exportChunk = exportChunk, + exportBase64Chunk = exportBase64Chunk, + exportHexChunk = exportHexChunk, + + crc32 = crc32, + getLength = getLength, + getByteLength = getByteLength, + getPointer = getPointer, + setPointer = setPointer, + setPointerFromEnd = setPointerFromEnd, + getPointerByte = getPointerByte, + setPointerByte = setPointerByte, + setPointerByteFromEnd = setPointerByteFromEnd, + isFinished = isFinished, + + writeBits = writeBits, + writeByte = writeByte, + writeBytesFast = writeBytesFast, + writeUnsigned = writeUnsigned, + writeSigned = writeSigned, + writeFloat = writeFloat, + writeBase64 = writeBase64, + writeString = writeString, + writeTerminatedString = writeTerminatedString, + writeSetLengthString = writeSetLengthString, + writeField = writeField, + + writeUInt8 = writeUInt8, + writeUInt16 = writeUInt16, + writeUInt32 = writeUInt32, + writeInt8 = writeInt8, + writeInt16 = writeInt16, + writeInt32 = writeInt32, + + writeFloat16 = writeFloat16, + writeFloat32 = writeFloat32, + writeFloat64 = writeFloat64, + + writeBrickColor = writeBrickColor, + writeColor3 = writeColor3, + writeCFrame = writeCFrame, + writeVector3 = writeVector3, + writeVector2 = writeVector2, + writeUDim2 = writeUDim2, + writeUDim = writeUDim, + writeRay = writeRay, + writeRect = writeRect, + writeRegion3 = writeRegion3, + writeEnum = writeEnum, + writeNumberRange = writeNumberRange, + writeNumberSequence = writeNumberSequence, + writeColorSequence = writeColorSequence, + + readBits = readBits, + readByte = readByte, + readUnsigned = readUnsigned, + readSigned = readSigned, + readFloat = readFloat, + readString = readString, + readTerminatedString = readTerminatedString, + readSetLengthString = readSetLengthString, + readField = readField, + readBytesFast = readBytesFast, + skipStrayBits = skipStrayBits, + + readUInt8 = readUInt8, + readUInt16 = readUInt16, + readUInt32 = readUInt32, + readInt8 = readInt8, + readInt16 = readInt16, + readInt32 = readInt32, + + readFloat16 = readFloat16, + readFloat32 = readFloat32, + readFloat64 = readFloat64, + + readBrickColor = readBrickColor, + readColor3 = readColor3, + readCFrame = readCFrame, + readVector3 = readVector3, + readVector2 = readVector2, + readUDim2 = readUDim2, + readUDim = readUDim, + readRay = readRay, + readRect = readRect, + readRegion3 = readRegion3, + readEnum = readEnum, + readNumberRange = readNumberRange, + readNumberSequence = readNumberSequence, + readColorSequence = readColorSequence, + } +end + +return bitBuffer \ No newline at end of file diff --git a/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/Node.lua b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/Node.lua new file mode 100644 index 0000000..9e0434a --- /dev/null +++ b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/Node.lua @@ -0,0 +1,38 @@ +local Node = {} +Node.__index = Node + +function Node.new(data) + local node = setmetatable({}, Node) + node.data = data + node.left = nil + node.right = nil + return node +end + +-- return an iterator that traverses the tree in order +function Node:inorder() + local stack = {} + local current = {self, ""} + table.insert(stack, current) + + return function() + while current[1].left do + local parent = current + current = {parent[1].left, parent[2] .. "0"} + table.insert(stack, current) + end + + if #stack > 0 then + local node = table.remove(stack) + + if node[1].right then + local parent = node + current = {parent[1].right, parent[2] .. "1"} + table.insert(stack, current) + end + return node[1], node[2] + end + end +end + +return Node \ No newline at end of file diff --git a/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/PriorityQueue.lua b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/PriorityQueue.lua new file mode 100644 index 0000000..c8a7604 --- /dev/null +++ b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/PriorityQueue.lua @@ -0,0 +1,232 @@ +--[[ + + PriorityQueue - v1.0.1 - public domain Lua priority queue + implemented with indirect binary heap + no warranty implied; use at your own risk + + based on binaryheap library (github.com/iskolbin/binaryheap) + + author: Ilya Kolbin (iskolbin@gmail.com) + url: github.com/iskolbin/priorityqueue + + See documentation in README file. + + COMPATIBILITY + + Lua 5.1, 5.2, 5.3, LuaJIT 1, 2 + + LICENSE + + This software is dual-licensed to the public domain and under the following + license: you are granted a perpetual, irrevocable license to copy, modify, + publish, and distribute this file as you see fit. + +--]] + +local floor, setmetatable = math.floor, setmetatable + +local function siftup( self, from ) + local items, priorities, indices, higherpriority = self, self._priorities, self._indices, self._higherpriority + local index = from + local parent = floor( index / 2 ) + while index > 1 and higherpriority( priorities[index], priorities[parent] ) do + priorities[index], priorities[parent] = priorities[parent], priorities[index] + items[index], items[parent] = items[parent], items[index] + indices[items[index]], indices[items[parent]] = index, parent + index = parent + parent = floor( index / 2 ) + end + return index +end + +local function siftdown( self, limit ) + local items, priorities, indices, higherpriority, size = self, self._priorities, self._indices, self._higherpriority, self._size + for index = limit, 1, -1 do + local left = index + index + local right = left + 1 + while left <= size do + local smaller = left + if right <= size and higherpriority( priorities[right], priorities[left] ) then + smaller = right + end + if higherpriority( priorities[smaller], priorities[index] ) then + items[index], items[smaller] = items[smaller], items[index] + priorities[index], priorities[smaller] = priorities[smaller], priorities[index] + indices[items[index]], indices[items[smaller]] = index, smaller + else + break + end + index = smaller + left = index + index + right = left + 1 + end + end +end + +local PriorityQueueMt + +local PriorityQueue = {} + +local function minishigher( a, b ) + return a < b +end + +local function maxishigher( a, b ) + return a > b +end + +function PriorityQueue.new( priority_or_array ) + local t = type( priority_or_array ) + local higherpriority = minishigher + + if t == 'table' then + higherpriority = priority_or_array.higherpriority or higherpriority + elseif t == 'function' or t == 'string' then + higherpriority = priority_or_array + elseif t ~= 'nil' then + local msg = 'Wrong argument type to PriorityQueue.new, it must be table or function or string, has: %q' + error( msg:format( t )) + end + + if type( higherpriority ) == 'string' then + if higherpriority == 'min' then + higherpriority = minishigher + elseif higherpriority == 'max' then + higherpriority = maxishigher + else + local msg = 'Wrong string argument to PriorityQueue.new, it must be "min" or "max", has: %q' + error( msg:format( tostring( higherpriority ))) + end + end + + local self = setmetatable( { + _priorities = {}, + _indices = {}, + _size = 0, + _higherpriority = higherpriority or minishigher + }, PriorityQueueMt ) + + if t == 'table' then + self:batchenq( priority_or_array ) + end + + return self +end + +function PriorityQueue:enqueue( item, priority ) + local items, priorities, indices = self, self._priorities, self._indices + if indices[item] ~= nil then + error( 'Item ' .. tostring(indices[item]) .. ' is already in the heap' ) + end + local size = self._size + 1 + self._size = size + items[size], priorities[size], indices[item] = item, priority, size + siftup( self, size ) + return self +end + +function PriorityQueue:remove( item ) + local index = self._indices[item] + if index ~= nil then + local size = self._size + local items, priorities, indices = self, self._priorities, self._indices + indices[item] = nil + if size == index then + items[size], priorities[size] = nil, nil + self._size = size - 1 + else + local lastitem = items[size] + items[index], priorities[index] = items[size], priorities[size] + items[size], priorities[size] = nil, nil + indices[lastitem] = index + size = size - 1 + self._size = size + if size > 1 then + siftdown( self, siftup( self, index )) + end + end + return true + else + return false + end +end + +function PriorityQueue:contains( item ) + return self._indices[item] ~= nil +end + +function PriorityQueue:update( item, priority ) + local ok = self:remove( item ) + if ok then + self:enqueue( item, priority ) + return true + else + return false + end +end + +function PriorityQueue:dequeue() + local size = self._size + + assert( size > 0, 'Heap is empty' ) + + local items, priorities, indices = self, self._priorities, self._indices + local item, priority = items[1], priorities[1] + indices[item] = nil + + if size > 1 then + local newitem = items[size] + items[1], priorities[1] = newitem, priorities[size] + items[size], priorities[size] = nil, nil + indices[newitem] = 1 + size = size - 1 + self._size = size + siftdown( self, 1 ) + else + items[1], priorities[1] = nil, nil + self._size = 0 + end + + return item, priority +end + +function PriorityQueue:peek() + return self[1], self._priorities[1] +end + +function PriorityQueue:len() + return self._size +end + +function PriorityQueue:empty() + return self._size <= 0 +end + +function PriorityQueue:batchenq( iparray ) + local items, priorities, indices = self, self._priorities, self._indices + local size = self._size + for i = 1, #iparray, 2 do + local item, priority = iparray[i], iparray[i+1] + if indices[item] ~= nil then + error( 'Item ' .. tostring(indices[item]) .. ' is already in the heap' ) + end + size = size + 1 + items[size], priorities[size] = item, priority + indices[item] = size + end + self._size = size + if size > 1 then + siftdown( self, floor( size / 2 )) + end +end + +PriorityQueueMt = { + __index = PriorityQueue, + __len = PriorityQueue.len, +} + +return setmetatable( PriorityQueue, { + __call = function( _, ... ) + return PriorityQueue.new( ... ) + end +} ) \ No newline at end of file diff --git a/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/init.lua b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/init.lua new file mode 100644 index 0000000..a18422f --- /dev/null +++ b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/Huffman/init.lua @@ -0,0 +1,114 @@ +--Huffman.lua +--iiau, Sat May 18 2024 +--Implementation of huffman coding algorithm for use in Roblox + +local Huffman = {} +local PriorityQueue = require(script.PriorityQueue) +local Node = require(script.Node) +local BitBuffer = require(script.BitBuffer) + +local CHUNK_SIZE = 256 + +--thanks to https://stackoverflow.com/a/32220398 for helping me with this +local function to_bin(n) + local t = {} + for _ = 1, 32 do + n = bit32.rrotate(n, -1) + table.insert(t, bit32.band(n, 1)) + end + return table.concat(t) +end + +-- Encode a string to huffman coded string. Limitation is that the data should not be more than 16777215 bytes. +-- @param data The string to encode +-- @return The encoded string +Huffman.encode = function(data: string) : string + assert(#data > 0, "Data must not be empty") + local buffer = BitBuffer() + + -- get the frequency of each character in the string + local freq, dict, size = {}, {}, 0 + for c in data:gmatch(".") do + freq[c] = (freq[c] or 0) + 1 + end + for _ in pairs(freq) do + size += 1 + end + + local q = PriorityQueue.new 'min' + for k: string, v: number in pairs(freq) do + local leaf = Node.new(string.byte(k)) + q:enqueue(leaf, v) + end + + while q:len() > 1 do + local left, freq_l = q:dequeue() + local right, freq_r = q:dequeue() + local parent = Node.new() + parent.left = left + parent.right = right + + q:enqueue(parent, freq_l + freq_r) + end + local tree = q:dequeue() + buffer.writeUInt8(size-1) + buffer.writeUnsigned(24, #data) + for node, bits: string in tree:inorder() do + if not node.data then + continue + end + local number = tonumber(bits, 2) + local bit_array = string.split(bits, "") + for i = 1, #bit_array do + bit_array[i] = tonumber(bit_array[i]) + end + + dict[string.char(node.data)] = bit_array + buffer.writeUInt8(node.data) -- char + buffer.writeUnsigned(5, #bits) -- number of bits + buffer.writeUnsigned(#bits, number) -- bits + end + for c in data:gmatch(".") do + buffer.writeBits(table.unpack(dict[c])) + end + + -- to avoid the dreaded too many results to unpack error + local chunks = {} + for _, chunk in buffer.exportChunk(CHUNK_SIZE) do + table.insert(chunks, chunk) + end + return table.concat(chunks) +end + +-- Decode a string from huffman coded string +-- @param data The string to decode +-- @return The decoded string +Huffman.decode = function(data: string) : string + assert(#data > 0, "Data must not be empty") + local buffer = BitBuffer(data) + + local dict_size = buffer.readUInt8()+1 + local len_data = buffer.readUnsigned(24) + local dict, read = {}, 0 + + for i = 1, dict_size do + local char = buffer.readUInt8() + local digits = buffer.readUnsigned(5) + local bits = buffer.readUnsigned(digits) + dict[to_bin(bits):sub(-digits)] = char + end + local decoded = {} + local bits = "" + while read < len_data do + bits ..= buffer.readBits(1)[1] + local char = dict[bits] + if char then + table.insert(decoded, string.char(char)) + bits = "" + read += 1 + end + end + return table.concat(decoded) +end + +return Huffman \ No newline at end of file diff --git a/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/LZW.lua b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/LZW.lua new file mode 100644 index 0000000..e97c029 --- /dev/null +++ b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/LZW.lua @@ -0,0 +1,170 @@ +-- Module by 1waffle1 and boatbomber, optimized and fixed by iiau +-- https://devforum.roblox.com/t/text-compression/163637/37 + +local dictionary = {} -- key to len + +do -- populate dictionary + local length = 0 + for i = 32, 127 do + if i ~= 34 and i ~= 92 then + local c = string.char(i) + dictionary[c] = length + dictionary[length] = c + length = length + 1 + end + end +end + +local escapemap_126, escapemap_127 = {}, {} +local unescapemap_126, unescapemap_127 = {}, {} + +local blacklisted_126 = { 34, 92 } +for i = 126, 180 do + table.insert(blacklisted_126, i) +end + +do -- Populate escape map + -- represents the numbers 0-31, 34, 92, 126, 127 (36 characters) + -- and 128-180 (52 characters) + -- https://devforum.roblox.com/t/text-compression/163637/5 + for i = 0, 31 + #blacklisted_126 do + local b = blacklisted_126[i - 31] + local s = i + 32 + + -- Note: 126 and 127 are magic numbers + local c = string.char(b or i) + local e = string.char(s + (s >= 34 and 1 or 0) + (s >= 91 and 1 or 0)) + + escapemap_126[c] = e + unescapemap_126[e] = c + end + + for i = 1, 255 - 180 do + local c = string.char(i + 180) + local s = i + 34 + local e = string.char(s + (s >= 92 and 1 or 0)) + + escapemap_127[c] = e + unescapemap_127[e] = c + end +end + +local function escape(s) + -- escape the control characters 0-31, double quote 34, backslash 92, Tilde 126, and DEL 127 (36 chars) + -- escape characters 128-180 (53 chars) + return string.gsub(string.gsub(s, '[%c"\\\126-\180]', function(c) + return "\126" .. escapemap_126[c] + end), '[\181-\255]', function(c) + return "\127" .. escapemap_127[c] + end) +end +local function unescape(s) + return string.gsub(string.gsub(s, "\127(.)", function(e) + return unescapemap_127[e] + end), "\126(.)", function(e) + return unescapemap_126[e] + end) +end + +local b93Cache = {} +local function tobase93(n) + local value = b93Cache[n] + if value then + return value + end + + local c = n + value = "" + repeat + local remainder = n % 93 + value = dictionary[remainder] .. value + n = (n - remainder) / 93 + until n == 0 + + b93Cache[c] = value + return value +end + +local b10Cache = {} +local function tobase10(value) + local n = b10Cache[value] + if n then + return n + end + + n = 0 + for i = 1, #value do + n = n + math.pow(93, i - 1) * dictionary[string.sub(value, -i, -i)] + end + + b10Cache[value] = n + return n +end + +local function compress(text) + assert(type(text) == "string", "bad argument #1 to 'compress' (string expected, got " .. typeof(text) .. ")") + local dictionaryCopy = table.clone(dictionary) + local key, sequence, size = "", {}, #dictionary + + local width, spans, span = 1, {}, 0 + local function listkey(k) + local value = tobase93(dictionaryCopy[k]) + local valueLength = #value + if valueLength > width then + width, span, spans[width] = valueLength, 0, span + end + table.insert(sequence, string.rep(" ", width - valueLength) .. value) + span += 1 + end + text = escape(text) + for i = 1, #text do + local c = string.sub(text, i, i) + local new = key .. c + if dictionaryCopy[new] then + key = new + else + listkey(key) + key = c + size += 1 + dictionaryCopy[new] = size + dictionaryCopy[size] = new + end + end + listkey(key) + spans[width] = span + return table.concat(spans, ",") .. "|" .. table.concat(sequence) +end + +local function decompress(text) + assert(type(text) == "string", "bad argument #1 to 'decompress' (string expected, got " .. typeof(text) .. ")") + local dictionaryCopy = table.clone(dictionary) + local sequence, spans, content = {}, string.match(text, "(.-)|(.*)") + local groups, start = {}, 1 + for span in string.gmatch(spans, "%d+") do + local width = #groups + 1 + groups[width] = string.sub(content, start, start + span * width - 1) + start = start + span * width + end + local previous + + for width, group in ipairs(groups) do + for value in string.gmatch(group, string.rep(".", width)) do + local entry = dictionaryCopy[tobase10(value)] + if previous then + if entry then + table.insert(dictionaryCopy, previous .. string.sub(entry, 1, 1)) + else + entry = previous .. string.sub(previous, 1, 1) + table.insert(dictionaryCopy, entry) + end + table.insert(sequence, entry) + else + sequence[1] = entry + end + previous = entry + end + end + return unescape(table.concat(sequence)) +end + +return { compress = compress, decompress = decompress } \ No newline at end of file diff --git a/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/init.lua b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/init.lua new file mode 100644 index 0000000..37cab5b --- /dev/null +++ b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/Deflate/init.lua @@ -0,0 +1,19 @@ +--Deflate.lua +--iiau, Sat May 18 2024 + +local Deflate = {} + +local LZW = require(script.LZW) +local Huffman = require(script.Huffman) + +Deflate.encode = function(data: string) : string + data = LZW.compress(data) + return Huffman.encode(data) +end + +Deflate.decode = function(data: string) : string + data = Huffman.decode(data) + return LZW.decompress(data) +end + +return Deflate \ No newline at end of file diff --git a/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/init.lua b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/init.lua new file mode 100644 index 0000000..8715741 --- /dev/null +++ b/src/ServerScriptService/Actor/ServerChunkManager/TerrainGen/init.lua @@ -0,0 +1,65 @@ +local TerrainGen = {} + +local deflate = require("./TerrainGen/Deflate") + +local DSS = game:GetService("DataStoreService") +local WORLDNAME = "DEFAULT" +local WORLDID = "b73bb5a6-297d-4352-b637-daec7e8c8f3e" +local Store = DSS:GetDataStore("BlockscraftWorldV1", WORLDID) + +local ChunkManager = require(game:GetService("ReplicatedStorage"):WaitForChild("Shared").ChunkManager) +local Chunk = require(game:GetService("ReplicatedStorage"):WaitForChild("Shared").ChunkManager.Chunk) + +TerrainGen.ServerChunkCache = {} :: {[typeof("")]: typeof(Chunk.new(0,0,0))} + +-- Load a chunk from the DataStore or generate it if not found +function TerrainGen:GetChunk(x, y, z) + + -- Generate a new chunk if it doesn't exist + local chunk = Chunk.new(x, y, z) + if y == 1 then + for cx = 1, 8 do + for cz = 1, 8 do + --local perlin = math.noise(((x*8)+cx)/100,((z*8)+cz)/100) + chunk:CreateBlock(cx, 1, cz, { id = 1, state = {} }) + --chunk:CreateBlock(x, 2, z, { id = 1, state = {} }) + end + end + end + if y == 0 then + for cx = 1, 8 do + for cy = 1, 8 do + for cz = 1, 8 do + --local perlin = math.noise(((x*8)+cx)/100,((z*8)+cz)/100) + chunk:CreateBlock(cx, cy, cz, { id = 2, state = {} }) + --chunk:CreateBlock(x, 2, z, { id = 1, state = {} }) + end + end + end + end + + return chunk +end + +-- Fake Chunk +function TerrainGen:GetFakeChunk(x, y, z) + + -- Generate a new chunk if it doesn't exist + local chunk = Chunk.new(x, y, z) + for cy = 1,8 do + for cx = 1, 8 do + for cz = 1, 8 do + --local perlin = math.noise(((x*8)+cx)/100,((z*8)+cz)/100) + chunk:CreateBlock(cx, cy, cz, { id = -2, state = {} }) + --chunk:CreateBlock(x, 2, z, { id = 1, state = {} }) + end + end + end + + return chunk +end + + +TerrainGen.CM = ChunkManager + +return TerrainGen diff --git a/src/ServerScriptService/Actor/ServerChunkManager/init.server.lua b/src/ServerScriptService/Actor/ServerChunkManager/init.server.lua new file mode 100644 index 0000000..2d309a4 --- /dev/null +++ b/src/ServerScriptService/Actor/ServerChunkManager/init.server.lua @@ -0,0 +1,107 @@ +print("Hello world!") + +task.synchronize() + +local ReplicatedStorage = game:GetService("ReplicatedStorage") + + +local Shared = ReplicatedStorage:WaitForChild("Shared") +local ModsFolder = ReplicatedStorage:WaitForChild("Mods") + +local PlacementManager = require(Shared.PlacementManager) +local TG = require("./ServerChunkManager/TerrainGen") + +do + local workspaceModFolder = game:GetService("Workspace"):WaitForChild("mods") + + for _,v in pairs(workspaceModFolder:GetChildren()) do + v.Parent = ModsFolder + end + workspaceModFolder:Destroy() +end + +local ML = require(Shared.ModLoader) +ML.loadModsS() + +do + local bv = Instance.new("BoolValue") + bv.Name = "MLLoaded" + bv.Value = true + bv.Parent = ReplicatedStorage:WaitForChild("Objects") +end + +local MAX_CHUNK_DIST = 200 + +local FakeChunk = TG:GetFakeChunk(-5,-5,-5) + +task.synchronize() + +ReplicatedStorage.Tick.OnServerEvent:Connect(function(player: Player, v: string) + if TG.ServerChunkCache[v] then + pcall(function() + TG.ServerChunkCache[v].inhabitedTime = tick() + end) + end +end) + +ReplicatedStorage.RecieveChunkPacket.OnServerInvoke = function(plr: Player, x: number, y: number, z: number) + -- validate xyz type and limit + if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then + return {} + end + + if math.abs(x) > MAX_CHUNK_DIST or math.abs(y) > MAX_CHUNK_DIST or math.abs(z) > MAX_CHUNK_DIST then + return FakeChunk.data + end + + task.desynchronize() + local chunk = TG:GetChunk(x, y, z) + local chunkdata = chunk.data + task.synchronize() + + return chunkdata + +end + +local tickRemote = ReplicatedStorage.Tick +local function propogate(a, cx, cy, cz, x, y, z, bd) + tickRemote:FireAllClients(a, cx, cy, cz, x, y, z, bd) +end + +ReplicatedStorage.PlaceBlock.OnServerEvent:Connect(function(player, cx, cy, cz, x, y, z, blockData) + --print("place",player, cx, cy, cz, x, y, z, blockData) + + if typeof(cx) ~= "number" or typeof(cy) ~= "number" or typeof(cz) ~= "number" then + return + end + if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then + return + end + if math.abs(cx) > MAX_CHUNK_DIST or math.abs(cy) > MAX_CHUNK_DIST or math.abs(cz) > MAX_CHUNK_DIST then + --return + end + + --local chunk = TG:GetChunk(cx, cy, cz) + --PlacementManager:PlaceBlockLocal(cx, cy, cz, x, y, z, blockData) + propogate("B_C", cx, cy, cz, x, y, z, blockData) +end) + +ReplicatedStorage.BreakBlock.OnServerEvent:Connect(function(player, cx, cy, cz, x, y, z) + --print("del",player, cx, cy, cz, x, y, z) + + if typeof(cx) ~= "number" or typeof(cy) ~= "number" or typeof(cz) ~= "number" then + return + end + if typeof(x) ~= "number" or typeof(y) ~= "number" or typeof(z) ~= "number" then + return + end + if math.abs(cx) > MAX_CHUNK_DIST or math.abs(cy) > MAX_CHUNK_DIST or math.abs(cz) > MAX_CHUNK_DIST then + return + end + + --local chunk = TG:GetChunk(cx, cy, cz) + --PlacementManager:BreakBlockLocal(cx, cy, cz, x, y, z) + propogate("B_D", cx, cy, cz, x, y, z, 0) +end) + +task.desynchronize() \ No newline at end of file diff --git a/src/ServerScriptService/Actor/init.meta.json b/src/ServerScriptService/Actor/init.meta.json new file mode 100644 index 0000000..6af4553 --- /dev/null +++ b/src/ServerScriptService/Actor/init.meta.json @@ -0,0 +1,4 @@ +{ + "className": "Actor", + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/StarterGui/Crosshair/LocalScript.client.lua b/src/StarterGui/Crosshair/LocalScript.client.lua new file mode 100644 index 0000000..92b726b --- /dev/null +++ b/src/StarterGui/Crosshair/LocalScript.client.lua @@ -0,0 +1,22 @@ +-- force first person mode on the person's camera + +if not game:IsLoaded() then + game.Loaded:Wait() +end + +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local UIS = game:GetService("UserInputService") + +ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded") + +game:GetService("Players").LocalPlayer.CameraMode = Enum.CameraMode.LockFirstPerson +UIS.MouseIconEnabled = false + +UIS.InputEnded:Connect(function(k) + if k.KeyCode == Enum.KeyCode.M then + local v = not script.Parent.DummyButton.Modal + UIS.MouseIconEnabled = v + script.Parent.CrosshairLabel.Visible = not v + script.Parent.DummyButton.Modal = v + end +end) \ No newline at end of file diff --git a/src/StarterGui/Crosshair/init.meta.json b/src/StarterGui/Crosshair/init.meta.json new file mode 100644 index 0000000..118e2ca --- /dev/null +++ b/src/StarterGui/Crosshair/init.meta.json @@ -0,0 +1,4 @@ +{ + "className": "ScreenGui", + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/StarterGui/Game_UI/LocalScript.client.lua b/src/StarterGui/Game_UI/LocalScript.client.lua new file mode 100644 index 0000000..7b92ec6 --- /dev/null +++ b/src/StarterGui/Game_UI/LocalScript.client.lua @@ -0,0 +1,79 @@ +if not game:IsLoaded() then + game.Loaded:Wait() +end + +local ui = script.Parent + +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local UIS = game:GetService("UserInputService") + +ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded") + +local cd = ReplicatedStorage.Objects.ChunkDebug:Clone() +local sky = ReplicatedStorage.Objects.Sky:Clone() +local base = ReplicatedStorage.Objects.FakeBaseplate:Clone() + + +cd.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain") +sky.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain") +base.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain") + +local PM = require(ReplicatedStorage.Shared.PlacementManager) + +UIS.InputEnded:Connect(function(input: InputObject, gameProcessedEvent: boolean) + if input.UserInputType == Enum.UserInputType.MouseButton1 then + local mouseBlock = PM:GetBlockAtMouse() + if not mouseBlock then return end + PM:BreakBlock(mouseBlock.chunk.X, mouseBlock.chunk.Y, mouseBlock.chunk.Z, mouseBlock.block.X, mouseBlock.block.Y, mouseBlock.block.Z) + elseif input.UserInputType == Enum.UserInputType.MouseButton2 then + local mouseBlock = PM:GetBlockAtMouse() + if not mouseBlock then return end + PM:PlaceBlock(mouseBlock.chunk.X, mouseBlock.chunk.Y, mouseBlock.chunk.Z, mouseBlock.block.X, mouseBlock.block.Y, mouseBlock.block.Z, { + id = 2, + state = {} + }) + end +end) + +game:GetService("RunService").RenderStepped:Connect(function(dt) + local fps = math.round(1/dt) + pcall(function() + -- pos in chunks of 32 studs of char + local pos = game:GetService("Players").LocalPlayer.Character:GetPivot() + local chunk = { + x = math.round(pos.X/32), + y = math.round(pos.Y/32), + z = math.round(pos.Z/32) + } + + if math.abs(chunk.x) == 0 then chunk.x = 0 end + if math.abs(chunk.y) == 0 then chunk.y = 0 end + if math.abs(chunk.z) == 0 then chunk.z = 0 end + + local bpos = { + x = math.round(pos.X/4), + y = math.round(pos.Y/4), + z = math.round(pos.Z/4) + } + + if math.abs(bpos.x) == 0 then bpos.x = 0 end + if math.abs(bpos.y) == 0 then bpos.y = 0 end + if math.abs(bpos.z) == 0 then bpos.z = 0 end + + sky.CFrame = pos + ui.DebugUpperText.Text = `Chunk {chunk.x} {chunk.y} {chunk.z}\nPos {bpos.x} {bpos.y} {bpos.z}\n{fps} FPS` + + cd:PivotTo(CFrame.new( + chunk.x*32, + chunk.y*32, + chunk.z*32 + )) + + base.CFrame = CFrame.new( + chunk.x*32, + -24, + chunk.z*32 + ) + + end) +end) \ No newline at end of file diff --git a/src/StarterGui/Game_UI/init.meta.json b/src/StarterGui/Game_UI/init.meta.json new file mode 100644 index 0000000..118e2ca --- /dev/null +++ b/src/StarterGui/Game_UI/init.meta.json @@ -0,0 +1,4 @@ +{ + "className": "ScreenGui", + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/StarterPlayer/StarterPlayerScripts/Actor/Init.client.lua b/src/StarterPlayer/StarterPlayerScripts/Actor/Init.client.lua new file mode 100644 index 0000000..b4f614a --- /dev/null +++ b/src/StarterPlayer/StarterPlayerScripts/Actor/Init.client.lua @@ -0,0 +1,25 @@ +if not game:IsLoaded() then + game.Loaded:Wait() +end + +pcall(function() + game:GetService("Workspace"):WaitForChild("$blockscraft_server",5):Destroy() +end) + +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded") + +local ML = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ModLoader")) + +ML.loadModsC() + +do + local PM = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("PlacementManager")) + PM:Init() +end + +do + local CM = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ChunkManager")) + CM:Init() +end \ No newline at end of file diff --git a/src/StarterPlayer/StarterPlayerScripts/Actor/init.meta.json b/src/StarterPlayer/StarterPlayerScripts/Actor/init.meta.json new file mode 100644 index 0000000..6af4553 --- /dev/null +++ b/src/StarterPlayer/StarterPlayerScripts/Actor/init.meta.json @@ -0,0 +1,4 @@ +{ + "className": "Actor", + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/Workspace/mods/init.meta.json b/src/Workspace/mods/init.meta.json new file mode 100644 index 0000000..1025b06 --- /dev/null +++ b/src/Workspace/mods/init.meta.json @@ -0,0 +1,3 @@ +{ + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/Workspace/mods/mc/init.lua b/src/Workspace/mods/mc/init.lua new file mode 100644 index 0000000..100fdc8 --- /dev/null +++ b/src/Workspace/mods/mc/init.lua @@ -0,0 +1,23 @@ +local mod = { + name = "Blockscraft", + description = "Base Blockscraft blocks", + ns = "mc", + author = { "ocbwoy3" } +} + +local rep = game:GetService("ReplicatedStorage"):WaitForChild("Blocks") +local upd = game:GetService("ReplicatedStorage"):WaitForChild("BlockUpdateOperations") + +function mod.init() + for a,b in pairs(script.blocks:GetChildren()) do + local upop = b:FindFirstChild("BlockUpdateOperation") + if upop then + upop.Name = b:GetAttribute("n") + upop:SetAttribute("n",b:GetAttribute("n")) + upop.Parent = upd + end + b:Clone().Parent = rep + end +end + +return mod \ No newline at end of file diff --git a/src/Workspace/mods/mc/init.meta.json b/src/Workspace/mods/mc/init.meta.json new file mode 100644 index 0000000..1025b06 --- /dev/null +++ b/src/Workspace/mods/mc/init.meta.json @@ -0,0 +1,3 @@ +{ + "ignoreUnknownInstances": true +} \ No newline at end of file diff --git a/src/Workspace/mods/mc/upds/BlockUpdateOperation.lua b/src/Workspace/mods/mc/upds/BlockUpdateOperation.lua new file mode 100644 index 0000000..7f0f2f4 --- /dev/null +++ b/src/Workspace/mods/mc/upds/BlockUpdateOperation.lua @@ -0,0 +1,3 @@ +return function(p: typeof(script.Parent.Parent.blocks["mc:grass_block"])) + p.block.Grass.Color = Color3.new(p:GetAttribute("x"),p:GetAttribute("x"),p:GetAttribute("x")) +end \ No newline at end of file diff --git a/src/Workspace/mods/mc/upds/init.meta.json b/src/Workspace/mods/mc/upds/init.meta.json new file mode 100644 index 0000000..1025b06 --- /dev/null +++ b/src/Workspace/mods/mc/upds/init.meta.json @@ -0,0 +1,3 @@ +{ + "ignoreUnknownInstances": true +} \ No newline at end of file