codex: stuff
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
place.rbxl.lock
|
place.rbxl.lock
|
||||||
|
Packages/
|
||||||
|
ServerPackages/
|
||||||
|
|||||||
@@ -5,11 +5,19 @@
|
|||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
"$className": "ReplicatedStorage",
|
"$className": "ReplicatedStorage",
|
||||||
"$ignoreUnknownInstances": true,
|
"$ignoreUnknownInstances": true,
|
||||||
|
"Packages": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"$path": "Packages"
|
||||||
|
},
|
||||||
"$path": "src/ReplicatedStorage"
|
"$path": "src/ReplicatedStorage"
|
||||||
},
|
},
|
||||||
"ServerScriptService": {
|
"ServerScriptService": {
|
||||||
"$className": "ServerScriptService",
|
"$className": "ServerScriptService",
|
||||||
"$ignoreUnknownInstances": true,
|
"$ignoreUnknownInstances": true,
|
||||||
|
"ServerPackages": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"$path": "ServerPackages"
|
||||||
|
},
|
||||||
"$path": "src/ServerScriptService"
|
"$path": "src/ServerScriptService"
|
||||||
},
|
},
|
||||||
"StarterGui": {
|
"StarterGui": {
|
||||||
@@ -32,4 +40,4 @@
|
|||||||
"$path": "src/Workspace"
|
"$path": "src/Workspace"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
local ChunkManager = {}
|
local ChunkManager = {}
|
||||||
|
|
||||||
local RunService = game:GetService("RunService")
|
local RunService = game:GetService("RunService")
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
|
||||||
local Chunk = require("./ChunkManager/Chunk")
|
local Chunk = require("./ChunkManager/Chunk")
|
||||||
local BlockManager = require("./ChunkManager/BlockManager")
|
local BlockManager = require("./ChunkManager/BlockManager")
|
||||||
@@ -15,10 +16,10 @@ ChunkFolder.Name = "$blockscraft_client"
|
|||||||
ChunkManager.ChunkFolder = ChunkFolder
|
ChunkManager.ChunkFolder = ChunkFolder
|
||||||
|
|
||||||
local CHUNK_RADIUS = 5
|
local CHUNK_RADIUS = 5
|
||||||
|
local INITIAL_SYNC_RADIUS = 2
|
||||||
local LOAD_BATCH = 8
|
local LOAD_BATCH = 8
|
||||||
local FORCELOAD_CHUNKS = {
|
local CHUNK_WORLD_SIZE = 32 -- 8 blocks * 4 studs
|
||||||
{0, 1, 0}
|
local FORCELOAD_CHUNKS = {}
|
||||||
}
|
|
||||||
|
|
||||||
local unloadingChunks = {}
|
local unloadingChunks = {}
|
||||||
local pendingChunkRequests = {}
|
local pendingChunkRequests = {}
|
||||||
@@ -37,6 +38,15 @@ do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function worldToChunkCoords(pos: Vector3): { x: number, y: number, z: number }
|
||||||
|
-- Align chunk boundaries so chunk 0 spans roughly [-16,16] with block centers every 4 studs.
|
||||||
|
return {
|
||||||
|
x = math.floor((pos.X + (CHUNK_WORLD_SIZE / 2)) / CHUNK_WORLD_SIZE),
|
||||||
|
y = math.floor((pos.Y + (CHUNK_WORLD_SIZE / 2)) / CHUNK_WORLD_SIZE),
|
||||||
|
z = math.floor((pos.Z + (CHUNK_WORLD_SIZE / 2)) / CHUNK_WORLD_SIZE),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
local function Swait(l)
|
local function Swait(l)
|
||||||
task.synchronize()
|
task.synchronize()
|
||||||
for _ = 1, l do
|
for _ = 1, l do
|
||||||
@@ -139,11 +149,7 @@ function ChunkManager:Tick()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local pos = player.Character:GetPivot().Position
|
local pos = player.Character:GetPivot().Position
|
||||||
local chunkPos = {
|
local chunkPos = worldToChunkCoords(pos)
|
||||||
x = math.round(pos.X / 32),
|
|
||||||
y = math.round(pos.Y / 32),
|
|
||||||
z = math.round(pos.Z / 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
task.defer(function()
|
task.defer(function()
|
||||||
local processed = 0
|
local processed = 0
|
||||||
@@ -207,6 +213,32 @@ function ChunkManager:Init()
|
|||||||
ChunkFolder.Parent = game:GetService("Workspace")
|
ChunkFolder.Parent = game:GetService("Workspace")
|
||||||
ChunkManager:ForceTick()
|
ChunkManager:ForceTick()
|
||||||
|
|
||||||
|
-- Synchronously warm a small area around the spawn chunk to ensure visible terrain on join.
|
||||||
|
local player = Players.LocalPlayer
|
||||||
|
local function warmInitial(character)
|
||||||
|
if not character then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local chunkPos = worldToChunkCoords(character:GetPivot().Position)
|
||||||
|
for _, offset in ipairs(CHUNK_OFFSETS) do
|
||||||
|
if offset[4] <= (INITIAL_SYNC_RADIUS * INITIAL_SYNC_RADIUS) then
|
||||||
|
local cx, cy, cz = chunkPos.x + offset[1], chunkPos.y + offset[2], chunkPos.z + offset[3]
|
||||||
|
local chunk = ChunkManager:GetChunk(cx, cy, cz)
|
||||||
|
chunk:Tick()
|
||||||
|
if not chunk.loaded then
|
||||||
|
ChunkManager:LoadChunk(cx, cy, cz)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if player.Character then
|
||||||
|
warmInitial(player.Character)
|
||||||
|
else
|
||||||
|
player.CharacterAdded:Wait()
|
||||||
|
warmInitial(player.Character)
|
||||||
|
end
|
||||||
|
|
||||||
task.defer(function()
|
task.defer(function()
|
||||||
while true do
|
while true do
|
||||||
wait(2)
|
wait(2)
|
||||||
|
|||||||
@@ -1,76 +1,137 @@
|
|||||||
local PlacementManager = {}
|
local PlacementManager = {}
|
||||||
|
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
local Workspace = game:GetService("Workspace")
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
local ChunkManager = require("./ChunkManager")
|
local ChunkManager = require("./ChunkManager")
|
||||||
local Util = require("./Util")
|
local Util = require("./Util")
|
||||||
|
|
||||||
PlacementManager.ChunkFolder = ChunkManager.ChunkFolder
|
local remotes = ReplicatedStorage:WaitForChild("Remotes")
|
||||||
|
local placeRemote = remotes:WaitForChild("PlaceBlock")
|
||||||
|
local breakRemote = remotes:WaitForChild("BreakBlock")
|
||||||
|
local tickRemote = ReplicatedStorage:WaitForChild("Tick")
|
||||||
|
|
||||||
|
local LOCAL_PLAYER = Players.LocalPlayer
|
||||||
|
local CAMERA = Workspace.CurrentCamera
|
||||||
|
|
||||||
|
local CHUNK_SIZE = 8
|
||||||
|
local BLOCK_SIZE = 4
|
||||||
|
local CHUNK_WORLD_SIZE = CHUNK_SIZE * BLOCK_SIZE
|
||||||
|
local MAX_RAY_DISTANCE = 1024
|
||||||
|
|
||||||
local raycastParams = RaycastParams.new()
|
local raycastParams = RaycastParams.new()
|
||||||
raycastParams.FilterDescendantsInstances = {PlacementManager.ChunkFolder}
|
raycastParams.FilterDescendantsInstances = { ChunkManager.ChunkFolder }
|
||||||
raycastParams.FilterType = Enum.RaycastFilterType.Include
|
raycastParams.FilterType = Enum.RaycastFilterType.Include
|
||||||
raycastParams.IgnoreWater = true
|
raycastParams.IgnoreWater = true
|
||||||
|
|
||||||
if _G.SB then return nil end
|
local selectionBox = Instance.new("SelectionBox")
|
||||||
_G.SB = true
|
selectionBox.Name = "$SelectionBox"
|
||||||
|
selectionBox.LineThickness = 0.03
|
||||||
|
selectionBox.Color3 = Color3.new(1, 1, 0)
|
||||||
|
selectionBox.SurfaceTransparency = 0.85
|
||||||
|
selectionBox.Transparency = 0.25
|
||||||
|
selectionBox.Adornee = nil
|
||||||
|
selectionBox.Parent = Workspace:FindFirstChildOfClass("Terrain") or Workspace
|
||||||
|
|
||||||
PlacementManager.SelectionBox = script.SelectionBox:Clone()
|
local function normalToId(normal: Vector3): Enum.NormalId
|
||||||
PlacementManager.SelectionBox.Name = "$SelectionBox"..(game:GetService("RunService"):IsServer() and "_SERVER" or "")
|
local absX, absY, absZ = math.abs(normal.X), math.abs(normal.Y), math.abs(normal.Z)
|
||||||
PlacementManager.SelectionBox.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
|
if absX > absY and absX > absZ then
|
||||||
|
return normal.X > 0 and Enum.NormalId.Right or Enum.NormalId.Left
|
||||||
-- Trash method TODO: Fix this
|
elseif absY > absX and absY > absZ then
|
||||||
local function findParent(i: Instance): Instance
|
return normal.Y > 0 and Enum.NormalId.Top or Enum.NormalId.Bottom
|
||||||
local f = i:FindFirstAncestorOfClass("Folder")
|
else
|
||||||
local d = i
|
return normal.Z > 0 and Enum.NormalId.Back or Enum.NormalId.Front
|
||||||
repeat
|
|
||||||
d = d.Parent
|
|
||||||
until d.Parent == f
|
|
||||||
return d
|
|
||||||
end
|
|
||||||
|
|
||||||
local Mouse: Mouse = nil
|
|
||||||
local lastNormalId: Enum.NormalId? = nil
|
|
||||||
|
|
||||||
local function normalIdToOffset(normal: Enum.NormalId): Vector3
|
|
||||||
if normal == Enum.NormalId.Top then
|
|
||||||
return Vector3.new(0, 1, 0)
|
|
||||||
elseif normal == Enum.NormalId.Bottom then
|
|
||||||
return Vector3.new(0, -1, 0)
|
|
||||||
elseif normal == Enum.NormalId.Left then
|
|
||||||
return Vector3.new(-1, 0, 0)
|
|
||||||
elseif normal == Enum.NormalId.Right then
|
|
||||||
return Vector3.new(1, 0, 0)
|
|
||||||
elseif normal == Enum.NormalId.Back then
|
|
||||||
return Vector3.new(0, 0, 1)
|
|
||||||
elseif normal == Enum.NormalId.Front then
|
|
||||||
return Vector3.new(0, 0, -1)
|
|
||||||
end
|
end
|
||||||
return Vector3.new(0, 0, 0)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function offsetChunkBlock(chunk: Vector3, block: Vector3, offset: Vector3)
|
local function findChunkAndBlock(hitInstance: Instance)
|
||||||
local cx, cy, cz = chunk.X, chunk.Y, chunk.Z
|
-- Find chunk container (child of ChunkFolder)
|
||||||
local bx, by, bz = block.X + offset.X, block.Y + offset.Y, block.Z + offset.Z
|
local inst = hitInstance
|
||||||
|
local chunkInstance = nil
|
||||||
|
while inst and inst.Parent do
|
||||||
|
if inst.Parent == ChunkManager.ChunkFolder then
|
||||||
|
chunkInstance = inst
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if inst.Parent.Parent == ChunkManager.ChunkFolder then
|
||||||
|
chunkInstance = inst.Parent
|
||||||
|
break
|
||||||
|
end
|
||||||
|
inst = inst.Parent
|
||||||
|
end
|
||||||
|
if not chunkInstance or chunkInstance.Parent ~= ChunkManager.ChunkFolder then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find block container (direct child of chunk)
|
||||||
|
local blockInstance = nil
|
||||||
|
inst = hitInstance
|
||||||
|
while inst and inst.Parent do
|
||||||
|
if inst.Parent == chunkInstance then
|
||||||
|
blockInstance = inst
|
||||||
|
break
|
||||||
|
end
|
||||||
|
inst = inst.Parent
|
||||||
|
end
|
||||||
|
if not blockInstance then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local chunkCoords = Util.BlockPosStringToCoords(chunkInstance.Name)
|
||||||
|
local blockCoords = nil
|
||||||
|
|
||||||
|
if blockInstance:IsA("BasePart") or blockInstance:IsA("Model") then
|
||||||
|
blockCoords = Util.BlockPosStringToCoords(blockInstance.Name)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not blockCoords then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return chunkCoords, blockCoords, blockInstance
|
||||||
|
end
|
||||||
|
|
||||||
|
local function offsetForPlacement(chunk: Vector3, block: Vector3, normalId: Enum.NormalId)
|
||||||
|
local cx, cy, cz = chunk.X, chunk.Y, chunk.Z
|
||||||
|
local bx, by, bz = block.X, block.Y, block.Z
|
||||||
|
|
||||||
|
if normalId == Enum.NormalId.Top then
|
||||||
|
by += 1
|
||||||
|
elseif normalId == Enum.NormalId.Bottom then
|
||||||
|
by -= 1
|
||||||
|
elseif normalId == Enum.NormalId.Left then
|
||||||
|
bx -= 1
|
||||||
|
elseif normalId == Enum.NormalId.Right then
|
||||||
|
bx += 1
|
||||||
|
elseif normalId == Enum.NormalId.Front then
|
||||||
|
bz -= 1
|
||||||
|
elseif normalId == Enum.NormalId.Back then
|
||||||
|
bz += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wrap across chunk boundaries (chunks are 1-indexed blocks 1..8)
|
||||||
if bx < 1 then
|
if bx < 1 then
|
||||||
bx = 8
|
bx = CHUNK_SIZE
|
||||||
cx -= 1
|
cx -= 1
|
||||||
elseif bx > 8 then
|
elseif bx > CHUNK_SIZE then
|
||||||
bx = 1
|
bx = 1
|
||||||
cx += 1
|
cx += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if by < 1 then
|
if by < 1 then
|
||||||
by = 8
|
by = CHUNK_SIZE
|
||||||
cy -= 1
|
cy -= 1
|
||||||
elseif by > 8 then
|
elseif by > CHUNK_SIZE then
|
||||||
by = 1
|
by = 1
|
||||||
cy += 1
|
cy += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if bz < 1 then
|
if bz < 1 then
|
||||||
bz = 8
|
bz = CHUNK_SIZE
|
||||||
cz -= 1
|
cz -= 1
|
||||||
elseif bz > 8 then
|
elseif bz > CHUNK_SIZE then
|
||||||
bz = 1
|
bz = 1
|
||||||
cz += 1
|
cz += 1
|
||||||
end
|
end
|
||||||
@@ -78,142 +139,107 @@ local function offsetChunkBlock(chunk: Vector3, block: Vector3, offset: Vector3)
|
|||||||
return Vector3.new(cx, cy, cz), Vector3.new(bx, by, bz)
|
return Vector3.new(cx, cy, cz), Vector3.new(bx, by, bz)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Gets the block and normalid of the block (and surface) the player is looking at
|
local lastHit = nil
|
||||||
function PlacementManager:Raycast()
|
|
||||||
if not Mouse then
|
local function castFromCamera()
|
||||||
Mouse = game:GetService("Players").LocalPlayer:GetMouse()
|
if not LOCAL_PLAYER or not CAMERA then
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
task.synchronize()
|
|
||||||
local objLookingAt = Mouse.Target
|
local mouse = LOCAL_PLAYER:GetMouse()
|
||||||
local dir = Mouse.TargetSurface
|
if not mouse then
|
||||||
if not objLookingAt then
|
return nil
|
||||||
PlacementManager.SelectionBox.Adornee = nil
|
|
||||||
script.RaycastResult.Value = nil
|
|
||||||
lastNormalId = nil
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--if not objLookingAt:IsDescendantOf(ChunkManager.ChunkFolder) then return end
|
local unitRay = mouse.UnitRay
|
||||||
local parent = findParent(objLookingAt)
|
local result = Workspace:Raycast(unitRay.Origin, unitRay.Direction * MAX_RAY_DISTANCE, raycastParams)
|
||||||
if parent:GetAttribute("ns") == true then
|
if not result then
|
||||||
PlacementManager.SelectionBox.Adornee = nil
|
selectionBox.Adornee = nil
|
||||||
script.RaycastResult.Value = nil
|
lastHit = nil
|
||||||
lastNormalId = nil
|
return nil
|
||||||
return
|
|
||||||
end
|
end
|
||||||
PlacementManager.SelectionBox.Adornee = parent
|
|
||||||
script.RaycastResult.Value = parent
|
local chunkCoords, blockCoords, blockInstance = findChunkAndBlock(result.Instance)
|
||||||
lastNormalId = dir
|
if not chunkCoords or not blockCoords then
|
||||||
return parent, dir
|
selectionBox.Adornee = nil
|
||||||
|
lastHit = nil
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local hitNormalId = normalToId(result.Normal)
|
||||||
|
local adornTarget = blockInstance
|
||||||
|
if adornTarget and adornTarget:IsA("Model") then
|
||||||
|
adornTarget = adornTarget.PrimaryPart or adornTarget:FindFirstChildWhichIsA("BasePart")
|
||||||
|
end
|
||||||
|
selectionBox.Adornee = adornTarget or result.Instance
|
||||||
|
|
||||||
|
lastHit = {
|
||||||
|
chunk = chunkCoords,
|
||||||
|
block = blockCoords,
|
||||||
|
normal = hitNormalId,
|
||||||
|
instance = adornTarget or result.Instance,
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastHit
|
||||||
end
|
end
|
||||||
|
|
||||||
function PlacementManager:RaycastGetResult()
|
-- Public API
|
||||||
return script.RaycastResult.Value
|
|
||||||
|
function PlacementManager:GetBlockAtMouse()
|
||||||
|
local hit = castFromCamera()
|
||||||
|
if not hit then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
chunk = hit.chunk,
|
||||||
|
block = hit.block,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local remotes = game:GetService("ReplicatedStorage"):WaitForChild("Remotes")
|
function PlacementManager:GetPlacementAtMouse()
|
||||||
local placeRemote = remotes:WaitForChild("PlaceBlock")
|
local hit = castFromCamera()
|
||||||
local breakRemote = remotes:WaitForChild("BreakBlock")
|
if not hit then
|
||||||
local tickRemote = game:GetService("ReplicatedStorage").Tick
|
return nil
|
||||||
|
end
|
||||||
|
local placeChunk, placeBlock = offsetForPlacement(hit.chunk, hit.block, hit.normal)
|
||||||
|
return {
|
||||||
|
chunk = placeChunk,
|
||||||
|
block = placeBlock,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
-- FIRES REMOTE
|
|
||||||
function PlacementManager:PlaceBlock(cx, cy, cz, x, y, z, blockId: string)
|
function PlacementManager:PlaceBlock(cx, cy, cz, x, y, z, blockId: string)
|
||||||
--print("placeblock")
|
|
||||||
--local chunk = ChunkManager:GetChunk(cx, cy, cz)
|
|
||||||
--chunk:CreateBlock(x, y, z, blockData)
|
|
||||||
placeRemote:FireServer(cx, cy, cz, x, y, z, blockId)
|
placeRemote:FireServer(cx, cy, cz, x, y, z, blockId)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- FIRES REMOTE
|
|
||||||
function PlacementManager:BreakBlock(cx, cy, cz, x, y, z)
|
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)
|
breakRemote:FireServer(cx, cy, cz, x, y, z)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- CLIENTSIDED
|
|
||||||
function PlacementManager:PlaceBlockLocal(cx, cy, cz, x, y, z, blockData)
|
function PlacementManager:PlaceBlockLocal(cx, cy, cz, x, y, z, blockData)
|
||||||
local chunk = ChunkManager:GetChunk(cx, cy, cz)
|
local chunk = ChunkManager:GetChunk(cx, cy, cz)
|
||||||
chunk:CreateBlock(x, y, z, blockData)
|
chunk:CreateBlock(x, y, z, blockData)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- CLIENTSIDED
|
|
||||||
function PlacementManager:BreakBlockLocal(cx, cy, cz, x, y, z)
|
function PlacementManager:BreakBlockLocal(cx, cy, cz, x, y, z)
|
||||||
local chunk = ChunkManager:GetChunk(cx, cy, cz)
|
local chunk = ChunkManager:GetChunk(cx, cy, cz)
|
||||||
chunk:RemoveBlock(x, y, z)
|
chunk:RemoveBlock(x, y, z)
|
||||||
end
|
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
|
|
||||||
lastNormalId = nil
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
if not selectedPart.Parent then
|
|
||||||
PlacementManager.SelectionBox.Adornee = nil
|
|
||||||
script.RaycastResult.Value = nil
|
|
||||||
lastNormalId = 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:GetTargetAtMouse(): nil | {chunk:Vector3, block: Vector3, normal: Enum.NormalId}
|
|
||||||
local hit = PlacementManager:GetBlockAtMouse()
|
|
||||||
if not hit or not lastNormalId then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
chunk = hit.chunk,
|
|
||||||
block = hit.block,
|
|
||||||
normal = lastNormalId
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function PlacementManager:GetPlacementAtMouse(): nil | {chunk:Vector3, block: Vector3}
|
|
||||||
local hit = PlacementManager:GetTargetAtMouse()
|
|
||||||
if not hit then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
local offset = normalIdToOffset(hit.normal)
|
|
||||||
local placeChunk, placeBlock = offsetChunkBlock(hit.chunk, hit.block, offset)
|
|
||||||
return {
|
|
||||||
chunk = placeChunk,
|
|
||||||
block = placeBlock
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function PlacementManager:Init()
|
function PlacementManager:Init()
|
||||||
game:GetService("RunService").Heartbeat:Connect(function()
|
RunService.Heartbeat:Connect(function()
|
||||||
local a,b = pcall(function()
|
local ok = pcall(castFromCamera)
|
||||||
PlacementManager:Raycast()
|
if not ok then
|
||||||
end)
|
selectionBox.Adornee = nil
|
||||||
if not a then
|
lastHit = nil
|
||||||
task.synchronize()
|
|
||||||
PlacementManager.SelectionBox.Adornee = nil
|
|
||||||
script.RaycastResult.Value = nil
|
|
||||||
task.desynchronize()
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
tickRemote.OnClientEvent:Connect(function(m, cx, cy, cz, x, y, z, d)
|
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
|
if m == "B_C" then
|
||||||
PlacementManager:PlaceBlockLocal(cx, cy, cz, x, y ,z, d)
|
PlacementManager:PlaceBlockLocal(cx, cy, cz, x, y, z, d)
|
||||||
end
|
elseif m == "B_D" then
|
||||||
if m == "B_D" then
|
PlacementManager:BreakBlockLocal(cx, cy, cz, x, y, z)
|
||||||
PlacementManager:BreakBlockLocal(cx, cy, cz, x, y ,z)
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,16 +3,62 @@ print("Hello world!")
|
|||||||
task.synchronize()
|
task.synchronize()
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local Workspace = game:GetService("Workspace")
|
||||||
|
local ServerScriptService = game:GetService("ServerScriptService")
|
||||||
|
|
||||||
|
local function getOrCreateFolder(parent: Instance, name: string): Folder
|
||||||
|
local existing = parent:FindFirstChild(name)
|
||||||
|
if existing then
|
||||||
|
return existing
|
||||||
|
end
|
||||||
|
local folder = Instance.new("Folder")
|
||||||
|
folder.Name = name
|
||||||
|
folder.Parent = parent
|
||||||
|
return folder
|
||||||
|
end
|
||||||
|
|
||||||
local Shared = ReplicatedStorage:WaitForChild("Shared")
|
local Shared = ReplicatedStorage:WaitForChild("Shared")
|
||||||
local ModsFolder = ReplicatedStorage:WaitForChild("Mods")
|
local ModsFolder = getOrCreateFolder(ReplicatedStorage, "Mods")
|
||||||
|
local ObjectsFolder = getOrCreateFolder(ReplicatedStorage, "Objects")
|
||||||
|
local BlocksFolder = getOrCreateFolder(ReplicatedStorage, "Blocks")
|
||||||
|
local BlockUpdateOpsFolder = getOrCreateFolder(ReplicatedStorage, "BlockUpdateOperations")
|
||||||
|
|
||||||
|
local function ensureBlock(id: number, name: string, color: Color3)
|
||||||
|
for _, child in ipairs(BlocksFolder:GetChildren()) do
|
||||||
|
if child:GetAttribute("n") == id then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
part.Name = name
|
||||||
|
part.Anchored = true
|
||||||
|
part.Size = Vector3.new(4, 4, 4)
|
||||||
|
part.Material = Enum.Material.SmoothPlastic
|
||||||
|
part.Color = color
|
||||||
|
part:SetAttribute("n", id)
|
||||||
|
part.Parent = BlocksFolder
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Seed minimal blocks so generation/placement never produce invalid ids.
|
||||||
|
ensureBlock(1, "mc:grass_block", Color3.fromRGB(117, 201, 112))
|
||||||
|
ensureBlock(2, "mc:dirt", Color3.fromRGB(134, 96, 67))
|
||||||
|
|
||||||
|
local ReplicaService = require(ServerScriptService:WaitForChild("ServerPackages"):WaitForChild("ReplicaService"))
|
||||||
|
local InventoryClassToken = ReplicaService.NewClassToken("Inventory")
|
||||||
|
|
||||||
local Util = require(Shared.Util)
|
local Util = require(Shared.Util)
|
||||||
local TG = require("./ServerChunkManager/TerrainGen")
|
local TG = require("./ServerChunkManager/TerrainGen")
|
||||||
|
|
||||||
|
local mlLoadedFlag = ObjectsFolder:FindFirstChild("MLLoaded")
|
||||||
|
if not mlLoadedFlag then
|
||||||
|
mlLoadedFlag = Instance.new("BoolValue")
|
||||||
|
mlLoadedFlag.Name = "MLLoaded"
|
||||||
|
mlLoadedFlag.Parent = ObjectsFolder
|
||||||
|
end
|
||||||
|
mlLoadedFlag.Value = false
|
||||||
|
|
||||||
do
|
do
|
||||||
local workspaceModFolder = game:GetService("Workspace"):WaitForChild("mods")
|
local workspaceModFolder = Workspace:FindFirstChild("mods") or getOrCreateFolder(Workspace, "mods")
|
||||||
|
|
||||||
for _,v in pairs(workspaceModFolder:GetChildren()) do
|
for _,v in pairs(workspaceModFolder:GetChildren()) do
|
||||||
v.Parent = ModsFolder
|
v.Parent = ModsFolder
|
||||||
@@ -23,12 +69,7 @@ end
|
|||||||
local ML = require(Shared.ModLoader)
|
local ML = require(Shared.ModLoader)
|
||||||
ML.loadModsS()
|
ML.loadModsS()
|
||||||
|
|
||||||
do
|
mlLoadedFlag.Value = true
|
||||||
local bv = Instance.new("BoolValue")
|
|
||||||
bv.Name = "MLLoaded"
|
|
||||||
bv.Value = true
|
|
||||||
bv.Parent = ReplicatedStorage:WaitForChild("Objects")
|
|
||||||
end
|
|
||||||
|
|
||||||
local MAX_CHUNK_DIST = 200
|
local MAX_CHUNK_DIST = 200
|
||||||
|
|
||||||
@@ -67,8 +108,6 @@ local tickRemote = ReplicatedStorage.Tick
|
|||||||
local remotes = ReplicatedStorage:WaitForChild("Remotes")
|
local remotes = ReplicatedStorage:WaitForChild("Remotes")
|
||||||
local placeRemote = remotes:WaitForChild("PlaceBlock")
|
local placeRemote = remotes:WaitForChild("PlaceBlock")
|
||||||
local breakRemote = remotes:WaitForChild("BreakBlock")
|
local breakRemote = remotes:WaitForChild("BreakBlock")
|
||||||
local inventorySync = remotes:WaitForChild("InventorySync")
|
|
||||||
local inventoryRequest = remotes:WaitForChild("InventoryRequest")
|
|
||||||
local blocksFolder = ReplicatedStorage:WaitForChild("Blocks")
|
local blocksFolder = ReplicatedStorage:WaitForChild("Blocks")
|
||||||
local function propogate(a, cx, cy, cz, x, y, z, bd)
|
local function propogate(a, cx, cy, cz, x, y, z, bd)
|
||||||
task.synchronize()
|
task.synchronize()
|
||||||
@@ -76,10 +115,11 @@ local function propogate(a, cx, cy, cz, x, y, z, bd)
|
|||||||
task.desynchronize()
|
task.desynchronize()
|
||||||
end
|
end
|
||||||
|
|
||||||
local MAX_REACH = 24
|
local MAX_REACH = math.huge
|
||||||
local HOTBAR_SIZE = 9
|
local HOTBAR_SIZE = 9
|
||||||
local blockIdMap = {}
|
local blockIdMap = {}
|
||||||
local playerInventories = {}
|
local playerInventories = {}
|
||||||
|
local playerReplicas = {}
|
||||||
|
|
||||||
local function rebuildBlockIdMap()
|
local function rebuildBlockIdMap()
|
||||||
table.clear(blockIdMap)
|
table.clear(blockIdMap)
|
||||||
@@ -109,7 +149,11 @@ local function buildDefaultSlots(): {string}
|
|||||||
end)
|
end)
|
||||||
local slots = table.create(HOTBAR_SIZE, "")
|
local slots = table.create(HOTBAR_SIZE, "")
|
||||||
for i = 1, HOTBAR_SIZE do
|
for i = 1, HOTBAR_SIZE do
|
||||||
slots[i] = ids[i]
|
local val = ids[i]
|
||||||
|
if val == nil or val == "" then
|
||||||
|
val = ""
|
||||||
|
end
|
||||||
|
slots[i] = val
|
||||||
end
|
end
|
||||||
return slots
|
return slots
|
||||||
end
|
end
|
||||||
@@ -119,8 +163,37 @@ local function syncInventory(player: Player)
|
|||||||
if not data then
|
if not data then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
task.synchronize()
|
if type(data.slots) ~= "table" then
|
||||||
inventorySync:FireClient(player, data.slots)
|
data.slots = buildDefaultSlots()
|
||||||
|
end
|
||||||
|
if type(data.allowed) ~= "table" then
|
||||||
|
data.allowed = {}
|
||||||
|
for i = 1, HOTBAR_SIZE do
|
||||||
|
local id = data.slots[i] or ""
|
||||||
|
if id ~= "" then
|
||||||
|
data.allowed[id] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
playerInventories[player.UserId] = data
|
||||||
|
end
|
||||||
|
local replica = playerReplicas[player]
|
||||||
|
if not replica then
|
||||||
|
replica = ReplicaService.NewReplica({
|
||||||
|
ClassToken = InventoryClassToken,
|
||||||
|
Tags = {
|
||||||
|
Player = player,
|
||||||
|
},
|
||||||
|
Data = {
|
||||||
|
Slots = data.slots,
|
||||||
|
Allowed = data.allowed,
|
||||||
|
},
|
||||||
|
Replication = player,
|
||||||
|
})
|
||||||
|
playerReplicas[player] = replica
|
||||||
|
else
|
||||||
|
replica:SetValue("Slots", data.slots)
|
||||||
|
replica:SetValue("Allowed", data.allowed)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function rebuildAllInventories()
|
local function rebuildAllInventories()
|
||||||
@@ -128,7 +201,7 @@ local function rebuildAllInventories()
|
|||||||
local slots = buildDefaultSlots()
|
local slots = buildDefaultSlots()
|
||||||
local allowed = {}
|
local allowed = {}
|
||||||
for i = 1, HOTBAR_SIZE do
|
for i = 1, HOTBAR_SIZE do
|
||||||
local id = slots[i]
|
local id = slots[i] or ""
|
||||||
if id ~= "" then
|
if id ~= "" then
|
||||||
allowed[id] = true
|
allowed[id] = true
|
||||||
end
|
end
|
||||||
@@ -147,12 +220,12 @@ blocksFolder.ChildRemoved:Connect(rebuildAllInventories)
|
|||||||
game:GetService("Players").PlayerAdded:Connect(function(player: Player)
|
game:GetService("Players").PlayerAdded:Connect(function(player: Player)
|
||||||
local slots = buildDefaultSlots()
|
local slots = buildDefaultSlots()
|
||||||
local allowed = {}
|
local allowed = {}
|
||||||
for i = 1, HOTBAR_SIZE do
|
for i = 1, HOTBAR_SIZE do
|
||||||
local id = slots[i]
|
local id = slots[i] or ""
|
||||||
if id ~= "" then
|
if id ~= "" then
|
||||||
allowed[id] = true
|
allowed[id] = true
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
playerInventories[player.UserId] = {
|
playerInventories[player.UserId] = {
|
||||||
slots = slots,
|
slots = slots,
|
||||||
allowed = allowed,
|
allowed = allowed,
|
||||||
@@ -162,17 +235,22 @@ end)
|
|||||||
|
|
||||||
game:GetService("Players").PlayerRemoving:Connect(function(player: Player)
|
game:GetService("Players").PlayerRemoving:Connect(function(player: Player)
|
||||||
playerInventories[player.UserId] = nil
|
playerInventories[player.UserId] = nil
|
||||||
|
local replica = playerReplicas[player]
|
||||||
|
if replica then
|
||||||
|
replica:Destroy()
|
||||||
|
playerReplicas[player] = nil
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
for _, player in ipairs(game:GetService("Players"):GetPlayers()) do
|
for _, player in ipairs(game:GetService("Players"):GetPlayers()) do
|
||||||
local slots = buildDefaultSlots()
|
local slots = buildDefaultSlots()
|
||||||
local allowed = {}
|
local allowed = {}
|
||||||
for i = 1, HOTBAR_SIZE do
|
for i = 1, HOTBAR_SIZE do
|
||||||
local id = slots[i]
|
local id = slots[i] or ""
|
||||||
if id ~= "" then
|
if id ~= "" then
|
||||||
allowed[id] = true
|
allowed[id] = true
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
playerInventories[player.UserId] = {
|
playerInventories[player.UserId] = {
|
||||||
slots = slots,
|
slots = slots,
|
||||||
allowed = allowed,
|
allowed = allowed,
|
||||||
@@ -180,10 +258,6 @@ for _, player in ipairs(game:GetService("Players"):GetPlayers()) do
|
|||||||
syncInventory(player)
|
syncInventory(player)
|
||||||
end
|
end
|
||||||
|
|
||||||
inventoryRequest.OnServerEvent:Connect(function(player: Player)
|
|
||||||
syncInventory(player)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local function getPlayerPosition(player: Player): Vector3?
|
local function getPlayerPosition(player: Player): Vector3?
|
||||||
local character = player.Character
|
local character = player.Character
|
||||||
if not character then
|
if not character then
|
||||||
@@ -197,12 +271,8 @@ local function getPlayerPosition(player: Player): Vector3?
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function isWithinReach(player: Player, cx: number, cy: number, cz: number, x: number, y: number, z: number): boolean
|
local function isWithinReach(player: Player, cx: number, cy: number, cz: number, x: number, y: number, z: number): boolean
|
||||||
local playerPos = getPlayerPosition(player)
|
-- Creative: disable reach limits.
|
||||||
if not playerPos then
|
return true
|
||||||
return false
|
|
||||||
end
|
|
||||||
local blockPos = Util.ChunkPosToCFrame(Vector3.new(cx, cy, cz), Vector3.new(x, y, z)).Position
|
|
||||||
return (blockPos - playerPos).Magnitude <= MAX_REACH
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function resolveBlockId(blockId: any): string | number | nil
|
local function resolveBlockId(blockId: any): string | number | nil
|
||||||
@@ -210,11 +280,8 @@ local function resolveBlockId(blockId: any): string | number | nil
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function playerHasBlockId(player: Player, blockId: string | number): boolean
|
local function playerHasBlockId(player: Player, blockId: string | number): boolean
|
||||||
local data = playerInventories[player.UserId]
|
-- Creative mode: allow all block ids.
|
||||||
if not data then
|
return true
|
||||||
return false
|
|
||||||
end
|
|
||||||
return data.allowed[tostring(blockId)] == true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getServerChunk(cx: number, cy: number, cz: number)
|
local function getServerChunk(cx: number, cy: number, cz: number)
|
||||||
|
|||||||
@@ -7,7 +7,14 @@ end
|
|||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local UIS = game:GetService("UserInputService")
|
local UIS = game:GetService("UserInputService")
|
||||||
|
|
||||||
ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded")
|
local function waitForModLoader()
|
||||||
|
local marker = ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded")
|
||||||
|
if marker:IsA("BoolValue") and marker.Value ~= true then
|
||||||
|
marker:GetPropertyChangedSignal("Value"):Wait()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
waitForModLoader()
|
||||||
|
|
||||||
game:GetService("Players").LocalPlayer.CameraMode = Enum.CameraMode.LockFirstPerson
|
game:GetService("Players").LocalPlayer.CameraMode = Enum.CameraMode.LockFirstPerson
|
||||||
UIS.MouseIconEnabled = false
|
UIS.MouseIconEnabled = false
|
||||||
@@ -19,4 +26,4 @@ UIS.InputEnded:Connect(function(k)
|
|||||||
script.Parent.CrosshairLabel.Visible = not v
|
script.Parent.CrosshairLabel.Visible = not v
|
||||||
script.Parent.DummyButton.Modal = v
|
script.Parent.DummyButton.Modal = v
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -5,54 +5,48 @@ end
|
|||||||
local ui = script.Parent
|
local ui = script.Parent
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
local Roact = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("Roact"))
|
||||||
|
local ReplicaController = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("ReplicaController"))
|
||||||
local Inventory = require(ReplicatedStorage.Shared.Inventory)
|
local Inventory = require(ReplicatedStorage.Shared.Inventory)
|
||||||
local Catppuccin = require(ReplicatedStorage.Shared.Catppuccin)
|
local Catppuccin = require(ReplicatedStorage.Shared.Catppuccin)
|
||||||
local blocksFolder = ReplicatedStorage:WaitForChild("Blocks")
|
local blocksFolder = ReplicatedStorage:WaitForChild("Blocks")
|
||||||
|
|
||||||
local mocha = Catppuccin.mocha
|
local mocha = Catppuccin.mocha
|
||||||
|
|
||||||
ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded")
|
local function waitForModLoader()
|
||||||
|
local marker = ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded")
|
||||||
|
if marker:IsA("BoolValue") and marker.Value ~= true then
|
||||||
|
marker:GetPropertyChangedSignal("Value"):Wait()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
waitForModLoader()
|
||||||
|
|
||||||
local cd = ReplicatedStorage.Objects.ChunkDebug:Clone()
|
local cd = ReplicatedStorage.Objects.ChunkDebug:Clone()
|
||||||
local sky = ReplicatedStorage.Objects.Sky:Clone()
|
local sky = ReplicatedStorage.Objects.Sky:Clone()
|
||||||
local base = ReplicatedStorage.Objects.FakeBaseplate:Clone()
|
local base = ReplicatedStorage.Objects.FakeBaseplate:Clone()
|
||||||
|
|
||||||
|
|
||||||
cd.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
|
cd.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
|
||||||
sky.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
|
sky.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
|
||||||
base.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
|
base.Parent = game:GetService("Workspace"):FindFirstChildOfClass("Terrain")
|
||||||
|
|
||||||
local hotbarRoot = ui:WaitForChild("Hotbar")
|
local debugUpperText = ui:FindFirstChild("DebugUpperText")
|
||||||
local hotbarSlotsRoot = hotbarRoot:WaitForChild("Frame")
|
if debugUpperText then
|
||||||
local hotbarSelectLabel = ui:WaitForChild("HotbarItemSelectLabel")
|
debugUpperText:Destroy()
|
||||||
local hotbarSelectText = hotbarSelectLabel:WaitForChild("TextLabel")
|
end
|
||||||
local hotbarDebug = ui:WaitForChild("HotbarDebug")
|
|
||||||
local hotbarDebugText = hotbarDebug:WaitForChild("TextLabel")
|
|
||||||
local debugUpperText = ui:WaitForChild("DebugUpperText")
|
|
||||||
local hotbarStroke = hotbarRoot:FindFirstChild("UIStroke")
|
|
||||||
local hotbarSelectStroke = hotbarSelectLabel:FindFirstChild("UIStroke")
|
|
||||||
local hotbarDebugStroke = hotbarDebug:FindFirstChild("UIStroke")
|
|
||||||
|
|
||||||
hotbarRoot.BackgroundColor3 = mocha.base
|
local function destroyLegacyUi(name)
|
||||||
if hotbarStroke then
|
local inst = ui:FindFirstChild(name)
|
||||||
hotbarStroke.Color = mocha.blue
|
if inst then
|
||||||
|
inst:Destroy()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
hotbarSelectLabel.BackgroundColor3 = mocha.base
|
|
||||||
if hotbarSelectStroke then
|
|
||||||
hotbarSelectStroke.Color = mocha.blue
|
|
||||||
end
|
|
||||||
hotbarDebug.BackgroundColor3 = mocha.base
|
|
||||||
if hotbarDebugStroke then
|
|
||||||
hotbarDebugStroke.Color = mocha.surface2
|
|
||||||
end
|
|
||||||
hotbarSelectText.TextColor3 = mocha.text
|
|
||||||
hotbarDebugText.TextColor3 = mocha.text
|
|
||||||
debugUpperText.TextColor3 = mocha.text
|
|
||||||
|
|
||||||
local slotFrames = {}
|
destroyLegacyUi("Hotbar")
|
||||||
local slotStrokes = {}
|
destroyLegacyUi("HotbarItemSelectLabel")
|
||||||
local slotLabels = {}
|
destroyLegacyUi("HotbarDebug")
|
||||||
local renderHotbar = nil
|
|
||||||
|
|
||||||
local blockDisplayNames = {}
|
local blockDisplayNames = {}
|
||||||
local blockIcons = {}
|
local blockIcons = {}
|
||||||
@@ -63,164 +57,364 @@ local function rebuildBlockMappings()
|
|||||||
for _, block in ipairs(blocksFolder:GetChildren()) do
|
for _, block in ipairs(blocksFolder:GetChildren()) do
|
||||||
local id = block:GetAttribute("n")
|
local id = block:GetAttribute("n")
|
||||||
if id ~= nil then
|
if id ~= nil then
|
||||||
blockDisplayNames[tostring(id)] = block.Name
|
local idKey = tostring(id)
|
||||||
|
blockDisplayNames[idKey] = block.Name
|
||||||
local icon = block:GetAttribute("icon")
|
local icon = block:GetAttribute("icon")
|
||||||
if typeof(icon) == "string" and icon ~= "" then
|
if typeof(icon) == "string" and icon ~= "" then
|
||||||
blockIcons[tostring(id)] = icon
|
blockIcons[idKey] = icon
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if renderHotbar then
|
|
||||||
renderHotbar()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
rebuildBlockMappings()
|
|
||||||
blocksFolder.ChildAdded:Connect(rebuildBlockMappings)
|
|
||||||
blocksFolder.ChildRemoved:Connect(rebuildBlockMappings)
|
|
||||||
|
|
||||||
for i = 1, Inventory.GetHotbarSize() do
|
|
||||||
local slotName = `HotbarSlot{i - 1}`
|
|
||||||
local slot = hotbarSlotsRoot:WaitForChild(slotName)
|
|
||||||
slotFrames[i] = slot
|
|
||||||
local stroke = slot:FindFirstChild("UIStroke")
|
|
||||||
if not stroke then
|
|
||||||
stroke = Instance.new("UIStroke")
|
|
||||||
stroke.Parent = slot
|
|
||||||
end
|
|
||||||
slotStrokes[i] = stroke
|
|
||||||
|
|
||||||
local imageLabel = slot:FindFirstChild("ImageLabel")
|
|
||||||
if not imageLabel then
|
|
||||||
imageLabel = Instance.new("ImageLabel")
|
|
||||||
imageLabel.Name = "ImageLabel"
|
|
||||||
imageLabel.BackgroundTransparency = 1
|
|
||||||
imageLabel.Size = UDim2.fromScale(1, 1)
|
|
||||||
imageLabel.ScaleType = Enum.ScaleType.Fit
|
|
||||||
imageLabel.Parent = slot
|
|
||||||
end
|
|
||||||
|
|
||||||
local label = slot:FindFirstChild("TextLabel")
|
|
||||||
if not label then
|
|
||||||
label = Instance.new("TextLabel")
|
|
||||||
label.Name = "TextLabel"
|
|
||||||
label.BackgroundTransparency = 1
|
|
||||||
label.Size = UDim2.fromScale(1, 1)
|
|
||||||
label.TextScaled = true
|
|
||||||
label.Font = Enum.Font.Gotham
|
|
||||||
label.TextColor3 = mocha.text
|
|
||||||
label.TextWrapped = true
|
|
||||||
label.ZIndex = 3
|
|
||||||
label.Parent = slot
|
|
||||||
end
|
|
||||||
slotLabels[i] = label
|
|
||||||
end
|
|
||||||
|
|
||||||
renderHotbar = function()
|
|
||||||
for i = 1, Inventory.GetHotbarSize() do
|
|
||||||
local slot = slotFrames[i]
|
|
||||||
if slot then
|
|
||||||
slot.BackgroundColor3 = mocha.surface0
|
|
||||||
local stroke = slotStrokes[i]
|
|
||||||
if stroke then
|
|
||||||
if i == Inventory.GetSelectedIndex() then
|
|
||||||
stroke.Color = mocha.blue
|
|
||||||
stroke.Thickness = 2
|
|
||||||
else
|
|
||||||
stroke.Color = mocha.surface2
|
|
||||||
stroke.Thickness = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local imageLabel = slot:FindFirstChild("ImageLabel")
|
|
||||||
local id = Inventory.GetSlot(i)
|
|
||||||
local displayName = id and (blockDisplayNames[tostring(id)] or tostring(id)) or ""
|
|
||||||
if imageLabel then
|
|
||||||
local icon = id and blockIcons[tostring(id)] or nil
|
|
||||||
imageLabel.Visible = icon ~= nil
|
|
||||||
imageLabel.Image = icon or ""
|
|
||||||
imageLabel.BackgroundTransparency = 1
|
|
||||||
imageLabel.ZIndex = 2
|
|
||||||
end
|
|
||||||
|
|
||||||
local textLabel = slotLabels[i]
|
|
||||||
if textLabel then
|
|
||||||
textLabel.Text = displayName
|
|
||||||
textLabel.TextColor3 = mocha.text
|
|
||||||
textLabel.Visible = displayName ~= ""
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function updateSelectedLabel()
|
local function getDisplayName(id)
|
||||||
local id = Inventory.GetSelectedId()
|
if id == nil then
|
||||||
local displayName = id and (blockDisplayNames[tostring(id)] or tostring(id)) or nil
|
return nil
|
||||||
if hotbarSelectText then
|
|
||||||
hotbarSelectText.Text = displayName and `Selected: {displayName}` or "Selected: (empty)"
|
|
||||||
end
|
|
||||||
if hotbarDebugText then
|
|
||||||
hotbarDebugText.Text = `Slot {Inventory.GetSelectedIndex()} Id {displayName or "empty"}`
|
|
||||||
end
|
end
|
||||||
|
return blockDisplayNames[tostring(id)] or tostring(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
local extraSlot = hotbarSlotsRoot and hotbarSlotsRoot:FindFirstChild("HotbarSlot9")
|
local HOTBAR_SLOTS = Inventory.GetHotbarSize()
|
||||||
if extraSlot then
|
local SLOT_SIZE = 40
|
||||||
extraSlot.Visible = false
|
local SLOT_PADDING = 6
|
||||||
|
local HOTBAR_PADDING = 6
|
||||||
|
local HOTBAR_WIDTH = (SLOT_SIZE * HOTBAR_SLOTS) + (SLOT_PADDING * (HOTBAR_SLOTS - 1)) + (HOTBAR_PADDING * 2)
|
||||||
|
local HOTBAR_HEIGHT = SLOT_SIZE + (HOTBAR_PADDING * 2)
|
||||||
|
local LABEL_WIDTH = 150
|
||||||
|
local LABEL_HEIGHT = 26
|
||||||
|
local HOTBAR_BOTTOM_OFFSET = 20
|
||||||
|
local LABEL_GAP = 8
|
||||||
|
|
||||||
|
local function isDevPlayer()
|
||||||
|
if RunService:IsStudio() then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
local player = Players.LocalPlayer
|
||||||
|
if game.CreatorType == Enum.CreatorType.User then
|
||||||
|
return player.UserId == game.CreatorId
|
||||||
|
end
|
||||||
|
if game.CreatorType == Enum.CreatorType.Group then
|
||||||
|
return player:IsInGroup(game.CreatorId)
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
renderHotbar()
|
local HotbarApp = Roact.Component:extend("HotbarApp")
|
||||||
updateSelectedLabel()
|
|
||||||
|
|
||||||
Inventory.OnChanged(function()
|
function HotbarApp:init()
|
||||||
renderHotbar()
|
self.state = {
|
||||||
updateSelectedLabel()
|
slots = table.clone(Inventory.GetSlots()),
|
||||||
end)
|
selectedIndex = Inventory.GetSelectedIndex(),
|
||||||
|
selectedName = getDisplayName(Inventory.GetSelectedId()) or "empty",
|
||||||
|
positionDebug = "",
|
||||||
|
inventoryDebug = "",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
Inventory.OnSelected(function()
|
function HotbarApp:didMount()
|
||||||
renderHotbar()
|
local function update()
|
||||||
updateSelectedLabel()
|
local selectedId = Inventory.GetSelectedId()
|
||||||
end)
|
local selectedDisplay = getDisplayName(selectedId) or "empty"
|
||||||
|
local filled = 0
|
||||||
|
for i = 1, HOTBAR_SLOTS do
|
||||||
|
if Inventory.GetSlot(i) ~= nil then
|
||||||
|
filled += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:setState({
|
||||||
|
slots = table.clone(Inventory.GetSlots()),
|
||||||
|
selectedIndex = Inventory.GetSelectedIndex(),
|
||||||
|
selectedName = selectedDisplay,
|
||||||
|
inventoryDebug = `Slot {Inventory.GetSelectedIndex()} Id {selectedDisplay} Filled {filled}/{HOTBAR_SLOTS}`,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
game:GetService("RunService").RenderStepped:Connect(function(dt)
|
rebuildBlockMappings()
|
||||||
local fps = math.round(1/dt)
|
update()
|
||||||
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
|
self._changedConn = Inventory.OnChanged(update)
|
||||||
if math.abs(bpos.y) == 0 then bpos.y = 0 end
|
self._selectedConn = Inventory.OnSelected(update)
|
||||||
if math.abs(bpos.z) == 0 then bpos.z = 0 end
|
self._blockAdded = blocksFolder.ChildAdded:Connect(function()
|
||||||
|
rebuildBlockMappings()
|
||||||
sky.CFrame = pos
|
update()
|
||||||
ui.DebugUpperText.Text = `Chunk {chunk.x} {chunk.y} {chunk.z}\nPos {bpos.x} {bpos.y} {bpos.z}\n<b>{fps} FPS</b>`
|
|
||||||
|
|
||||||
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)
|
||||||
end)
|
self._blockRemoved = blocksFolder.ChildRemoved:Connect(function()
|
||||||
|
rebuildBlockMappings()
|
||||||
|
update()
|
||||||
|
end)
|
||||||
|
|
||||||
|
self._renderConn = RunService.RenderStepped:Connect(function(dt)
|
||||||
|
local fps = math.round(1 / dt)
|
||||||
|
pcall(function()
|
||||||
|
local pos = 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
|
||||||
|
cd:PivotTo(CFrame.new(
|
||||||
|
chunk.x * 32,
|
||||||
|
chunk.y * 32,
|
||||||
|
chunk.z * 32
|
||||||
|
))
|
||||||
|
|
||||||
|
base.CFrame = CFrame.new(
|
||||||
|
chunk.x * 32,
|
||||||
|
-24,
|
||||||
|
chunk.z * 32
|
||||||
|
)
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
positionDebug = `Chunk {chunk.x} {chunk.y} {chunk.z}\nPos {bpos.x} {bpos.y} {bpos.z}\n<b>{fps} FPS</b>`,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function HotbarApp:willUnmount()
|
||||||
|
if self._changedConn then
|
||||||
|
self._changedConn:Disconnect()
|
||||||
|
self._changedConn = nil
|
||||||
|
end
|
||||||
|
if self._selectedConn then
|
||||||
|
self._selectedConn:Disconnect()
|
||||||
|
self._selectedConn = nil
|
||||||
|
end
|
||||||
|
if self._blockAdded then
|
||||||
|
self._blockAdded:Disconnect()
|
||||||
|
self._blockAdded = nil
|
||||||
|
end
|
||||||
|
if self._blockRemoved then
|
||||||
|
self._blockRemoved:Disconnect()
|
||||||
|
self._blockRemoved = nil
|
||||||
|
end
|
||||||
|
if self._renderConn then
|
||||||
|
self._renderConn:Disconnect()
|
||||||
|
self._renderConn = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function HotbarApp:render()
|
||||||
|
local slots = self.state.slots or {}
|
||||||
|
local selectedIndex = self.state.selectedIndex or 1
|
||||||
|
local selectedName = self.state.selectedName or "empty"
|
||||||
|
local positionDebug = self.state.positionDebug or ""
|
||||||
|
local inventoryDebug = self.state.inventoryDebug or ""
|
||||||
|
local debugText = positionDebug
|
||||||
|
if inventoryDebug ~= "" then
|
||||||
|
debugText ..= "\n" .. inventoryDebug
|
||||||
|
end
|
||||||
|
|
||||||
|
local slotChildren = {
|
||||||
|
Layout = Roact.createElement("UIListLayout", {
|
||||||
|
FillDirection = Enum.FillDirection.Horizontal,
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
Padding = UDim.new(0, SLOT_PADDING),
|
||||||
|
}),
|
||||||
|
Padding = Roact.createElement("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, HOTBAR_PADDING),
|
||||||
|
PaddingRight = UDim.new(0, HOTBAR_PADDING),
|
||||||
|
PaddingTop = UDim.new(0, HOTBAR_PADDING),
|
||||||
|
PaddingBottom = UDim.new(0, HOTBAR_PADDING),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, HOTBAR_SLOTS do
|
||||||
|
local id = slots[i]
|
||||||
|
local displayName = getDisplayName(id) or ""
|
||||||
|
local icon = id and blockIcons[tostring(id)] or nil
|
||||||
|
local isSelected = i == selectedIndex
|
||||||
|
|
||||||
|
slotChildren["Slot" .. i] = Roact.createElement("Frame", {
|
||||||
|
Name = "Slot" .. i,
|
||||||
|
Size = UDim2.fromOffset(SLOT_SIZE, SLOT_SIZE),
|
||||||
|
BackgroundColor3 = mocha.surface0,
|
||||||
|
LayoutOrder = i,
|
||||||
|
}, {
|
||||||
|
Corner = Roact.createElement("UICorner", {
|
||||||
|
CornerRadius = UDim.new(0, 8),
|
||||||
|
}),
|
||||||
|
Stroke = Roact.createElement("UIStroke", {
|
||||||
|
Color = isSelected and mocha.blue or mocha.surface2,
|
||||||
|
Thickness = isSelected and 2 or 1,
|
||||||
|
}),
|
||||||
|
Icon = Roact.createElement("ImageLabel", {
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.fromScale(1, 1),
|
||||||
|
Image = icon or "",
|
||||||
|
Visible = icon ~= nil,
|
||||||
|
ScaleType = Enum.ScaleType.Fit,
|
||||||
|
ZIndex = 2,
|
||||||
|
}),
|
||||||
|
Label = Roact.createElement("TextLabel", {
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.fromScale(1, 1),
|
||||||
|
Text = displayName,
|
||||||
|
TextColor3 = mocha.text,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
TextSize = 12,
|
||||||
|
TextWrapped = true,
|
||||||
|
Visible = icon == nil and displayName ~= "",
|
||||||
|
ZIndex = 3,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return Roact.createElement("Folder", nil, {
|
||||||
|
DebugUpperText = Roact.createElement("TextLabel", {
|
||||||
|
Name = "DebugUpperText",
|
||||||
|
AnchorPoint = Vector2.new(0, 0),
|
||||||
|
Position = UDim2.new(0, 12, 0, 12),
|
||||||
|
Size = UDim2.fromOffset(300, 90),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Text = debugText,
|
||||||
|
TextColor3 = mocha.red,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
TextSize = 14,
|
||||||
|
RichText = true,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextYAlignment = Enum.TextYAlignment.Top,
|
||||||
|
}),
|
||||||
|
SelectedLabel = Roact.createElement("TextLabel", {
|
||||||
|
Name = "HotbarItemLabel",
|
||||||
|
AnchorPoint = Vector2.new(0.5, 1),
|
||||||
|
Position = UDim2.new(0.5, 0, 1, -(HOTBAR_BOTTOM_OFFSET + HOTBAR_HEIGHT + LABEL_GAP)),
|
||||||
|
Size = UDim2.fromOffset(LABEL_WIDTH, LABEL_HEIGHT),
|
||||||
|
BackgroundColor3 = mocha.base,
|
||||||
|
Text = selectedName,
|
||||||
|
TextColor3 = mocha.text,
|
||||||
|
Font = Enum.Font.JosefinSans,
|
||||||
|
TextSize = 14,
|
||||||
|
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||||
|
}, {
|
||||||
|
Corner = Roact.createElement("UICorner", {
|
||||||
|
CornerRadius = UDim.new(0, 8),
|
||||||
|
}),
|
||||||
|
Stroke = Roact.createElement("UIStroke", {
|
||||||
|
Color = mocha.blue,
|
||||||
|
Thickness = 2,
|
||||||
|
ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
|
||||||
|
}),
|
||||||
|
Padding = Roact.createElement("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 10),
|
||||||
|
PaddingRight = UDim.new(0, 10),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Hotbar = Roact.createElement("Frame", {
|
||||||
|
Name = "Hotbar",
|
||||||
|
AnchorPoint = Vector2.new(0.5, 1),
|
||||||
|
Position = UDim2.new(0.5, 0, 1, -HOTBAR_BOTTOM_OFFSET),
|
||||||
|
Size = UDim2.fromOffset(HOTBAR_WIDTH, HOTBAR_HEIGHT),
|
||||||
|
BackgroundColor3 = mocha.base,
|
||||||
|
}, {
|
||||||
|
Corner = Roact.createElement("UICorner", {
|
||||||
|
CornerRadius = UDim.new(0, 12),
|
||||||
|
}),
|
||||||
|
Stroke = Roact.createElement("UIStroke", {
|
||||||
|
Color = mocha.blue,
|
||||||
|
Thickness = 2,
|
||||||
|
ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
|
||||||
|
}),
|
||||||
|
Slots = Roact.createElement("Frame", {
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.fromScale(1, 1),
|
||||||
|
}, slotChildren),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local ReplicaDebugger = Roact.Component:extend("ReplicaDebugger")
|
||||||
|
|
||||||
|
function ReplicaDebugger:init()
|
||||||
|
self._replicaCount = 0
|
||||||
|
self.state = {
|
||||||
|
replicaCount = 0,
|
||||||
|
initialData = ReplicaController.InitialDataReceived == true,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function ReplicaDebugger:didMount()
|
||||||
|
self._newReplicaConn = ReplicaController.NewReplicaSignal:Connect(function()
|
||||||
|
self._replicaCount += 1
|
||||||
|
self:setState({
|
||||||
|
replicaCount = self._replicaCount,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
self._initialConn = ReplicaController.InitialDataReceivedSignal:Connect(function()
|
||||||
|
self:setState({
|
||||||
|
initialData = true,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ReplicaDebugger:willUnmount()
|
||||||
|
if self._newReplicaConn then
|
||||||
|
self._newReplicaConn:Disconnect()
|
||||||
|
self._newReplicaConn = nil
|
||||||
|
end
|
||||||
|
if self._initialConn then
|
||||||
|
self._initialConn:Disconnect()
|
||||||
|
self._initialConn = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ReplicaDebugger:render()
|
||||||
|
return Roact.createElement("TextLabel", {
|
||||||
|
Name = "ReplicaDebugger",
|
||||||
|
AnchorPoint = Vector2.new(1, 0),
|
||||||
|
Position = UDim2.new(1, -12, 0, 12),
|
||||||
|
Size = UDim2.fromOffset(220, 60),
|
||||||
|
BackgroundColor3 = mocha.mantle,
|
||||||
|
BackgroundTransparency = 0.2,
|
||||||
|
Text = `Replica (client)\nInitial: {tostring(self.state.initialData)}\nReplicas: {self.state.replicaCount}`,
|
||||||
|
TextColor3 = mocha.text,
|
||||||
|
Font = Enum.Font.GothamMedium,
|
||||||
|
TextSize = 12,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextYAlignment = Enum.TextYAlignment.Top,
|
||||||
|
}, {
|
||||||
|
Corner = Roact.createElement("UICorner", {
|
||||||
|
CornerRadius = UDim.new(0, 8),
|
||||||
|
}),
|
||||||
|
Padding = Roact.createElement("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 8),
|
||||||
|
PaddingRight = UDim.new(0, 8),
|
||||||
|
PaddingTop = UDim.new(0, 6),
|
||||||
|
PaddingBottom = UDim.new(0, 6),
|
||||||
|
}),
|
||||||
|
Stroke = Roact.createElement("UIStroke", {
|
||||||
|
Color = mocha.surface2,
|
||||||
|
Thickness = 1,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
Roact.mount(Roact.createElement(HotbarApp), ui, "HotbarRoact")
|
||||||
|
|
||||||
|
if isDevPlayer() then
|
||||||
|
local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
|
||||||
|
local debugGui = Instance.new("ScreenGui")
|
||||||
|
debugGui.Name = "ReplicaDebugger"
|
||||||
|
debugGui.ResetOnSpawn = false
|
||||||
|
debugGui.IgnoreGuiInset = true
|
||||||
|
debugGui.Parent = playerGui
|
||||||
|
Roact.mount(Roact.createElement(ReplicaDebugger), debugGui, "ReplicaDebugger")
|
||||||
|
end
|
||||||
|
|||||||
@@ -9,7 +9,14 @@ end)
|
|||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded")
|
local function waitForModLoader()
|
||||||
|
local marker = ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded")
|
||||||
|
if marker:IsA("BoolValue") and marker.Value ~= true then
|
||||||
|
marker:GetPropertyChangedSignal("Value"):Wait()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
waitForModLoader()
|
||||||
|
|
||||||
local ML = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ModLoader"))
|
local ML = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ModLoader"))
|
||||||
|
|
||||||
@@ -5,13 +5,18 @@ end
|
|||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local UIS = game:GetService("UserInputService")
|
local UIS = game:GetService("UserInputService")
|
||||||
|
|
||||||
ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded")
|
local function waitForModLoader()
|
||||||
|
local marker = ReplicatedStorage:WaitForChild("Objects"):WaitForChild("MLLoaded")
|
||||||
|
if marker:IsA("BoolValue") and marker.Value ~= true then
|
||||||
|
marker:GetPropertyChangedSignal("Value"):Wait()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
waitForModLoader()
|
||||||
|
|
||||||
local PM = require(ReplicatedStorage.Shared.PlacementManager)
|
local PM = require(ReplicatedStorage.Shared.PlacementManager)
|
||||||
local Inventory = require(ReplicatedStorage.Shared.Inventory)
|
local Inventory = require(ReplicatedStorage.Shared.Inventory)
|
||||||
local remotes = ReplicatedStorage:WaitForChild("Remotes")
|
local ReplicaController = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("ReplicaController"))
|
||||||
local inventorySync = remotes:WaitForChild("InventorySync")
|
|
||||||
local inventoryRequest = remotes:WaitForChild("InventoryRequest")
|
|
||||||
|
|
||||||
local keyToSlot = {
|
local keyToSlot = {
|
||||||
[Enum.KeyCode.One] = 1,
|
[Enum.KeyCode.One] = 1,
|
||||||
@@ -36,14 +41,22 @@ local function findNextFilledIndex(startIndex: number, direction: number): numbe
|
|||||||
return startIndex
|
return startIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
inventorySync.OnClientEvent:Connect(function(slots)
|
local function applySlots(slots)
|
||||||
if typeof(slots) ~= "table" then
|
if typeof(slots) ~= "table" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
Inventory.SetSlots(slots)
|
Inventory.SetSlots(slots)
|
||||||
|
end
|
||||||
|
|
||||||
|
ReplicaController.ReplicaOfClassCreated("Inventory", function(replica)
|
||||||
|
applySlots(replica.Data.Slots)
|
||||||
|
local changeConn = replica:ListenToChange({"Slots"}, function(newSlots)
|
||||||
|
applySlots(newSlots)
|
||||||
|
end)
|
||||||
|
replica:AddCleanupTask(changeConn)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
inventoryRequest:FireServer()
|
ReplicaController.RequestData()
|
||||||
|
|
||||||
UIS.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
|
UIS.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
|
||||||
if gameProcessedEvent then
|
if gameProcessedEvent then
|
||||||
@@ -52,9 +65,7 @@ UIS.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
|
|||||||
|
|
||||||
local slot = keyToSlot[input.KeyCode]
|
local slot = keyToSlot[input.KeyCode]
|
||||||
if slot then
|
if slot then
|
||||||
if Inventory.GetSlot(slot) ~= nil then
|
Inventory.SetSelectedIndex(slot)
|
||||||
Inventory.SetSelectedIndex(slot)
|
|
||||||
end
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -87,7 +98,6 @@ UIS.InputChanged:Connect(function(input: InputObject, gameProcessedEvent: boolea
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
local direction = delta > 0 and -1 or 1
|
local direction = delta > 0 and -1 or 1
|
||||||
local nextIndex = findNextFilledIndex(Inventory.GetSelectedIndex(), direction)
|
Inventory.SetSelectedIndex(((Inventory.GetSelectedIndex() - 1 + direction) % Inventory.GetHotbarSize()) + 1)
|
||||||
Inventory.SetSelectedIndex(nextIndex)
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|||||||
23
wally.lock
Normal file
23
wally.lock
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# This file is automatically @generated by Wally.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
registry = "test"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "etheroit/replicacontroller"
|
||||||
|
version = "1.0.0"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "etheroit/replicaservice"
|
||||||
|
version = "1.0.2"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ocbwoy3/minecraft-roblox"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [["ReplicaController", "etheroit/replicacontroller@1.0.0"], ["Roact", "roblox/roact@1.4.4"], ["ReplicaService", "etheroit/replicaservice@1.0.2"]]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roblox/roact"
|
||||||
|
version = "1.4.4"
|
||||||
|
dependencies = []
|
||||||
15
wally.toml
Normal file
15
wally.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "ocbwoy3-development-studios/minecraft-roblox"
|
||||||
|
description = "A Roblox game inspired by Minecraft's world building system"
|
||||||
|
version = "0.1.0"
|
||||||
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
|
realm = "shared"
|
||||||
|
authors = ["ocbwoy3 <ocbwoy3@ocbwoy3.dev>"]
|
||||||
|
private = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
Roact = "roblox/roact@1.4.4"
|
||||||
|
ReplicaController = "etheroit/replicacontroller@1.0.0"
|
||||||
|
|
||||||
|
[server-dependencies]
|
||||||
|
ReplicaService = "etheroit/replicaservice@1.0.2"
|
||||||
Reference in New Issue
Block a user