core: fix hotbar
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
Packages/
|
Packages/
|
||||||
|
ServerPackages/
|
||||||
128
ReplicatedStorage/Shared/ClientState.lua
Normal file
128
ReplicatedStorage/Shared/ClientState.lua
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
--!native
|
||||||
|
--!optimize 2
|
||||||
|
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
|
if RunService:IsServer() then
|
||||||
|
error("ClientState can only be required on the client")
|
||||||
|
end
|
||||||
|
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
|
local Replica = require(ReplicatedStorage.Packages.replica)
|
||||||
|
|
||||||
|
local ClientState = {}
|
||||||
|
|
||||||
|
local HOTBAR_SIZE = 10
|
||||||
|
|
||||||
|
local localPlayer = Players.LocalPlayer
|
||||||
|
local replicaForPlayer = nil
|
||||||
|
local changed = Instance.new("BindableEvent")
|
||||||
|
|
||||||
|
local function fireChanged()
|
||||||
|
changed:Fire()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onReplicaNew(replica)
|
||||||
|
local tags = replica.Tags or {}
|
||||||
|
if tags.UserId ~= localPlayer.UserId and tags.Player ~= localPlayer then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
replicaForPlayer = replica
|
||||||
|
replica:OnChange(fireChanged)
|
||||||
|
fireChanged()
|
||||||
|
end
|
||||||
|
|
||||||
|
Replica.OnNew("ClientState", onReplicaNew)
|
||||||
|
Replica.RequestData()
|
||||||
|
|
||||||
|
function ClientState:IsReady(): boolean
|
||||||
|
return replicaForPlayer ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientState:GetReplica()
|
||||||
|
return replicaForPlayer
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientState:GetSelectedSlot(): number?
|
||||||
|
if not replicaForPlayer then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return replicaForPlayer.Data.selectedSlot
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getInventory()
|
||||||
|
return replicaForPlayer and replicaForPlayer.Data.inventory or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientState:GetItemInfo(blockId: any)
|
||||||
|
if not replicaForPlayer or not blockId then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local inv = getInventory()
|
||||||
|
local entry = inv and inv[tostring(blockId)]
|
||||||
|
if not entry then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
id = tostring(blockId),
|
||||||
|
name = entry.name or tostring(blockId),
|
||||||
|
count = entry.count,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientState:GetHotbarSlots(): {string}
|
||||||
|
if not replicaForPlayer then
|
||||||
|
local slots = table.create(HOTBAR_SIZE)
|
||||||
|
for i = 1, HOTBAR_SIZE do
|
||||||
|
slots[i] = ""
|
||||||
|
end
|
||||||
|
return slots
|
||||||
|
end
|
||||||
|
|
||||||
|
return replicaForPlayer.Data.hotbar or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientState:GetSlotInfo(slot: number)
|
||||||
|
if not replicaForPlayer then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local hotbar = replicaForPlayer.Data.hotbar
|
||||||
|
if not hotbar then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local id = hotbar[slot]
|
||||||
|
if not id then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return ClientState:GetItemInfo(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientState:GetSelectedBlock()
|
||||||
|
if not replicaForPlayer then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local slot = ClientState:GetSelectedSlot()
|
||||||
|
if not slot then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return ClientState:GetSlotInfo(slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientState:SetSelectedSlot(slot: number)
|
||||||
|
if not replicaForPlayer then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local hotbar = replicaForPlayer.Data.hotbar
|
||||||
|
if not hotbar or not hotbar[slot] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
replicaForPlayer:FireServer("SelectHotbarSlot", slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
ClientState.Changed = changed.Event
|
||||||
|
|
||||||
|
return ClientState
|
||||||
198
ServerScriptService/Actor/ClientState.lua
Normal file
198
ServerScriptService/Actor/ClientState.lua
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
--!native
|
||||||
|
--!optimize 2
|
||||||
|
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
|
local Replica = require(ReplicatedStorage.Packages.replica)
|
||||||
|
|
||||||
|
local ClientStateService = {}
|
||||||
|
|
||||||
|
local HOTBAR_SIZE = 10
|
||||||
|
|
||||||
|
local token = Replica.Token("ClientState")
|
||||||
|
|
||||||
|
local blockCatalog = {}
|
||||||
|
local playerReplicas = {} :: {[Player]: any}
|
||||||
|
local blocksFolder: Folder? = nil
|
||||||
|
local readyConnections = {} :: {[Player]: RBXScriptConnection}
|
||||||
|
|
||||||
|
local function sortBlocks()
|
||||||
|
table.sort(blockCatalog, function(a, b)
|
||||||
|
local na = tonumber(a.id)
|
||||||
|
local nb = tonumber(b.id)
|
||||||
|
if na and nb then
|
||||||
|
return na < nb
|
||||||
|
end
|
||||||
|
if na then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if nb then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return a.id < b.id
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rebuildBlockCatalog()
|
||||||
|
table.clear(blockCatalog)
|
||||||
|
if not blocksFolder then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, block in ipairs(blocksFolder:GetChildren()) do
|
||||||
|
local id = block:GetAttribute("n")
|
||||||
|
if id ~= nil then
|
||||||
|
table.insert(blockCatalog, {
|
||||||
|
id = tostring(id),
|
||||||
|
name = block:GetAttribute("displayName") or block:GetAttribute("dn") or block.Name,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
sortBlocks()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function makeBaseState()
|
||||||
|
local inventory = {}
|
||||||
|
local hotbar = {}
|
||||||
|
|
||||||
|
for _, entry in ipairs(blockCatalog) do
|
||||||
|
inventory[entry.id] = {
|
||||||
|
name = entry.name,
|
||||||
|
count = 999999,
|
||||||
|
}
|
||||||
|
if #hotbar < HOTBAR_SIZE then
|
||||||
|
table.insert(hotbar, entry.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
inventory = inventory,
|
||||||
|
hotbar = hotbar,
|
||||||
|
selectedSlot = #hotbar > 0 and 1 or 0,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sanitizeSelection(hotbar, selectedSlot)
|
||||||
|
if type(selectedSlot) ~= "number" then
|
||||||
|
return (#hotbar > 0) and 1 or 0
|
||||||
|
end
|
||||||
|
if selectedSlot < 1 or selectedSlot > HOTBAR_SIZE then
|
||||||
|
return (#hotbar > 0) and 1 or 0
|
||||||
|
end
|
||||||
|
if not hotbar[selectedSlot] then
|
||||||
|
return (#hotbar > 0) and 1 or 0
|
||||||
|
end
|
||||||
|
return selectedSlot
|
||||||
|
end
|
||||||
|
|
||||||
|
local function refreshReplica(replica)
|
||||||
|
local state = makeBaseState()
|
||||||
|
replica:Set({"inventory"}, state.inventory)
|
||||||
|
replica:Set({"hotbar"}, state.hotbar)
|
||||||
|
replica:Set({"selectedSlot"}, sanitizeSelection(state.hotbar, replica.Data.selectedSlot))
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientStateService:SetBlocksFolder(folder: Folder?)
|
||||||
|
blocksFolder = folder
|
||||||
|
rebuildBlockCatalog()
|
||||||
|
for _, replica in pairs(playerReplicas) do
|
||||||
|
refreshReplica(replica)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientStateService:GetReplica(player: Player)
|
||||||
|
return playerReplicas[player]
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientStateService:GetSelectedBlockId(player: Player)
|
||||||
|
local replica = playerReplicas[player]
|
||||||
|
if not replica then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local data = replica.Data
|
||||||
|
local hotbar = data.hotbar or {}
|
||||||
|
local selectedSlot = sanitizeSelection(hotbar, data.selectedSlot)
|
||||||
|
return hotbar[selectedSlot]
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientStateService:HasInInventory(player: Player, blockId: any): boolean
|
||||||
|
local replica = playerReplicas[player]
|
||||||
|
if not replica or not blockId then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local inv = replica.Data.inventory
|
||||||
|
return inv and inv[tostring(blockId)] ~= nil or false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handleReplicaEvents(player: Player, replica)
|
||||||
|
replica.OnServerEvent:Connect(function(plr, action, payload)
|
||||||
|
if plr ~= player then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if action == "SelectHotbarSlot" then
|
||||||
|
local slot = tonumber(payload)
|
||||||
|
local hotbar = replica.Data.hotbar
|
||||||
|
if not hotbar then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if slot and slot >= 1 and slot <= HOTBAR_SIZE and hotbar[slot] then
|
||||||
|
replica:Set({"selectedSlot"}, slot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onPlayerAdded(player: Player)
|
||||||
|
local replica = Replica.New({
|
||||||
|
Token = token,
|
||||||
|
Tags = {
|
||||||
|
UserId = player.UserId,
|
||||||
|
Player = player,
|
||||||
|
},
|
||||||
|
Data = makeBaseState(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if Replica.ReadyPlayers[player] then
|
||||||
|
replica:Subscribe(player)
|
||||||
|
else
|
||||||
|
readyConnections[player] = Replica.NewReadyPlayer:Connect(function(newPlayer)
|
||||||
|
if newPlayer ~= player then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if readyConnections[player] then
|
||||||
|
readyConnections[player]:Disconnect()
|
||||||
|
readyConnections[player] = nil
|
||||||
|
end
|
||||||
|
replica:Subscribe(player)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
handleReplicaEvents(player, replica)
|
||||||
|
playerReplicas[player] = replica
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onPlayerRemoving(player: Player)
|
||||||
|
local replica = playerReplicas[player]
|
||||||
|
if replica then
|
||||||
|
replica:Destroy()
|
||||||
|
playerReplicas[player] = nil
|
||||||
|
end
|
||||||
|
if readyConnections[player] then
|
||||||
|
readyConnections[player]:Disconnect()
|
||||||
|
readyConnections[player] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClientStateService:Init()
|
||||||
|
rebuildBlockCatalog()
|
||||||
|
|
||||||
|
for _, player in ipairs(Players:GetPlayers()) do
|
||||||
|
onPlayerAdded(player)
|
||||||
|
end
|
||||||
|
Players.PlayerAdded:Connect(onPlayerAdded)
|
||||||
|
Players.PlayerRemoving:Connect(onPlayerRemoving)
|
||||||
|
end
|
||||||
|
|
||||||
|
return ClientStateService
|
||||||
@@ -2,15 +2,39 @@
|
|||||||
--!optimize 2
|
--!optimize 2
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local ServerStorage = game:GetService("ServerStorage")
|
||||||
|
local ClientStateService = require(script.Parent.ClientState)
|
||||||
|
|
||||||
local Shared = ReplicatedStorage:WaitForChild("Shared")
|
local Shared = ReplicatedStorage:WaitForChild("Shared")
|
||||||
local ModsFolder = ReplicatedStorage:WaitForChild("Mods")
|
local ModsFolder = ReplicatedStorage:WaitForChild("Mods")
|
||||||
|
local BlocksFolderRS = ReplicatedStorage:FindFirstChild("Blocks") or Instance.new("Folder")
|
||||||
|
BlocksFolderRS.Name = "Blocks"
|
||||||
|
BlocksFolderRS.Parent = ReplicatedStorage
|
||||||
|
local BlocksFolderSS = ServerStorage:FindFirstChild("Blocks") or Instance.new("Folder")
|
||||||
|
BlocksFolderSS.Name = "Blocks"
|
||||||
|
BlocksFolderSS.Parent = ServerStorage
|
||||||
|
|
||||||
local Util = require(Shared.Util)
|
local Util = require(Shared.Util)
|
||||||
local TG = require("./ServerChunkManager/TerrainGen")
|
local TG = require(script.TerrainGen)
|
||||||
local Players = game:GetService("Players")
|
local Players = game:GetService("Players")
|
||||||
|
|
||||||
|
local blockIdMap = {}
|
||||||
|
local rebuildBlockIdMap
|
||||||
|
|
||||||
|
local function syncBlocksToServerStorage()
|
||||||
|
BlocksFolderSS:ClearAllChildren()
|
||||||
|
for _, child in ipairs(BlocksFolderRS:GetChildren()) do
|
||||||
|
child:Clone().Parent = BlocksFolderSS
|
||||||
|
end
|
||||||
|
ClientStateService:SetBlocksFolder(BlocksFolderSS)
|
||||||
|
if rebuildBlockIdMap then
|
||||||
|
rebuildBlockIdMap()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
BlocksFolderRS.ChildAdded:Connect(syncBlocksToServerStorage)
|
||||||
|
BlocksFolderRS.ChildRemoved:Connect(syncBlocksToServerStorage)
|
||||||
|
|
||||||
do
|
do
|
||||||
local workspaceModFolder = game:GetService("Workspace"):WaitForChild("mods")
|
local workspaceModFolder = game:GetService("Workspace"):WaitForChild("mods")
|
||||||
|
|
||||||
@@ -22,6 +46,8 @@ end
|
|||||||
|
|
||||||
local ML = require(Shared.ModLoader)
|
local ML = require(Shared.ModLoader)
|
||||||
ML.loadModsS()
|
ML.loadModsS()
|
||||||
|
syncBlocksToServerStorage()
|
||||||
|
ClientStateService:Init()
|
||||||
|
|
||||||
do
|
do
|
||||||
local bv = Instance.new("BoolValue")
|
local bv = Instance.new("BoolValue")
|
||||||
@@ -67,7 +93,7 @@ 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 blocksFolder = ReplicatedStorage:WaitForChild("Blocks")
|
local blocksFolder = BlocksFolderSS
|
||||||
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()
|
||||||
tickRemote:FireAllClients(a, cx, cy, cz, x, y, z, bd)
|
tickRemote:FireAllClients(a, cx, cy, cz, x, y, z, bd)
|
||||||
@@ -75,9 +101,8 @@ local function propogate(a, cx, cy, cz, x, y, z, bd)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local MAX_REACH = 512
|
local MAX_REACH = 512
|
||||||
local blockIdMap = {}
|
|
||||||
|
|
||||||
local function rebuildBlockIdMap()
|
rebuildBlockIdMap = function()
|
||||||
table.clear(blockIdMap)
|
table.clear(blockIdMap)
|
||||||
for _, block in ipairs(blocksFolder:GetChildren()) do
|
for _, block in ipairs(blocksFolder:GetChildren()) do
|
||||||
local id = block:GetAttribute("n")
|
local id = block:GetAttribute("n")
|
||||||
@@ -113,6 +138,17 @@ local function resolveBlockId(blockId: any): string | number | nil
|
|||||||
return blockIdMap[blockId]
|
return blockIdMap[blockId]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function playerCanUseBlock(player: Player, resolvedId: any): boolean
|
||||||
|
if not ClientStateService:HasInInventory(player, resolvedId) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local selected = ClientStateService:GetSelectedBlockId(player)
|
||||||
|
if not selected then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return tostring(selected) == tostring(resolvedId)
|
||||||
|
end
|
||||||
|
|
||||||
local function getServerChunk(cx: number, cy: number, cz: number)
|
local function getServerChunk(cx: number, cy: number, cz: number)
|
||||||
task.desynchronize()
|
task.desynchronize()
|
||||||
local chunk = TG:GetChunk(cx, cy, cz)
|
local chunk = TG:GetChunk(cx, cy, cz)
|
||||||
@@ -176,6 +212,9 @@ placeRemote.OnServerEvent:Connect(function(player, cx, cy, cz, x, y, z, blockId)
|
|||||||
if not resolvedId then
|
if not resolvedId then
|
||||||
return reject("invalid id")
|
return reject("invalid id")
|
||||||
end
|
end
|
||||||
|
if not playerCanUseBlock(player, resolvedId) then
|
||||||
|
return reject("not in inventory/hotbar")
|
||||||
|
end
|
||||||
|
|
||||||
local blockPos = Util.ChunkPosToCFrame(Vector3.new(cx, cy, cz), Vector3.new(x, y, z)).Position
|
local blockPos = Util.ChunkPosToCFrame(Vector3.new(cx, cy, cz), Vector3.new(x, y, z)).Position
|
||||||
if isBlockInsidePlayer(blockPos) then
|
if isBlockInsidePlayer(blockPos) then
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ local PM = require(ReplicatedStorage.Shared.PlacementManager)
|
|||||||
local BlockManager = require(ReplicatedStorage.Shared.ChunkManager.BlockManager)
|
local BlockManager = require(ReplicatedStorage.Shared.ChunkManager.BlockManager)
|
||||||
local PlacementState = require(ReplicatedStorage.Shared.PlacementState)
|
local PlacementState = require(ReplicatedStorage.Shared.PlacementState)
|
||||||
local Util = require(ReplicatedStorage.Shared.Util)
|
local Util = require(ReplicatedStorage.Shared.Util)
|
||||||
|
local ClientState = require(ReplicatedStorage.Shared.ClientState)
|
||||||
local blocksFolder = ReplicatedStorage:WaitForChild("Blocks")
|
|
||||||
|
|
||||||
local HOTBAR_SIZE = 10
|
local HOTBAR_SIZE = 10
|
||||||
|
|
||||||
@@ -53,33 +52,34 @@ local function isTextInputFocused(): boolean
|
|||||||
return config ~= nil and config.IsFocused
|
return config ~= nil and config.IsFocused
|
||||||
end
|
end
|
||||||
|
|
||||||
local function buildHotbarIds(): {string}
|
local function resolveSelectedSlot(slots, desired)
|
||||||
local ids = {}
|
if desired and desired >= 1 and desired <= HOTBAR_SIZE and slots[desired] ~= "" then
|
||||||
local names = {}
|
return desired
|
||||||
for _, block in ipairs(blocksFolder:GetChildren()) do
|
|
||||||
local id = block:GetAttribute("n")
|
|
||||||
if id ~= nil then
|
|
||||||
local n = tonumber(id)
|
|
||||||
if n and n > 0 then
|
|
||||||
local idStr = tostring(n)
|
|
||||||
table.insert(ids, idStr)
|
|
||||||
names[idStr] = block:GetAttribute("displayName") or block:GetAttribute("dn") or block.Name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
table.sort(ids, function(a, b)
|
|
||||||
local na = tonumber(a)
|
|
||||||
local nb = tonumber(b)
|
|
||||||
if na and nb then
|
|
||||||
return na < nb
|
|
||||||
end
|
|
||||||
return a < b
|
|
||||||
end)
|
|
||||||
local slots = table.create(HOTBAR_SIZE)
|
|
||||||
for i = 1, HOTBAR_SIZE do
|
for i = 1, HOTBAR_SIZE do
|
||||||
slots[i] = ids[i] or ""
|
if slots[i] and slots[i] ~= "" then
|
||||||
|
return i
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return slots, names
|
return desired or 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function buildHotbarFromState()
|
||||||
|
local slots = table.create(HOTBAR_SIZE)
|
||||||
|
local names = {}
|
||||||
|
|
||||||
|
for i = 1, HOTBAR_SIZE do
|
||||||
|
local info = ClientState:GetSlotInfo(i)
|
||||||
|
if info then
|
||||||
|
slots[i] = tostring(info.id)
|
||||||
|
names[slots[i]] = info.name or slots[i]
|
||||||
|
else
|
||||||
|
slots[i] = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local selected = resolveSelectedSlot(slots, ClientState:GetSelectedSlot())
|
||||||
|
return slots, names, selected
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ensurePreviewRig(part: Instance)
|
local function ensurePreviewRig(part: Instance)
|
||||||
@@ -131,40 +131,43 @@ end
|
|||||||
local Hotbar = Roact.Component:extend("Hotbar")
|
local Hotbar = Roact.Component:extend("Hotbar")
|
||||||
|
|
||||||
function Hotbar:init()
|
function Hotbar:init()
|
||||||
|
local slots, names, selected = buildHotbarFromState()
|
||||||
self.state = {
|
self.state = {
|
||||||
slots = nil,
|
slots = slots,
|
||||||
names = nil,
|
names = names,
|
||||||
selected = 1,
|
selected = selected,
|
||||||
}
|
}
|
||||||
local slots, names = buildHotbarIds()
|
|
||||||
self.state.slots = slots
|
|
||||||
self.state.names = names
|
|
||||||
local initialId = slots and slots[1] or ""
|
|
||||||
if initialId and initialId ~= "" then
|
|
||||||
local initialName = names and (names[initialId] or initialId) or initialId
|
|
||||||
PlacementState:SetSelected(initialId, initialName)
|
|
||||||
end
|
|
||||||
|
|
||||||
self._updateSlots = function()
|
self._syncFromClientState = function()
|
||||||
local nextSlots, nextNames = buildHotbarIds()
|
local nextSlots, nextNames, nextSelected = buildHotbarFromState()
|
||||||
|
nextSelected = resolveSelectedSlot(nextSlots, nextSelected or self.state.selected)
|
||||||
self:setState({
|
self:setState({
|
||||||
slots = nextSlots,
|
slots = nextSlots,
|
||||||
names = nextNames,
|
names = nextNames,
|
||||||
|
selected = nextSelected,
|
||||||
})
|
})
|
||||||
|
local id = nextSlots[nextSelected] or ""
|
||||||
|
local name = ""
|
||||||
|
if id ~= "" then
|
||||||
|
name = nextNames[id] or id
|
||||||
|
end
|
||||||
|
PlacementState:SetSelected(id, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
self._setSelected = function(slot: number)
|
self._setSelected = function(slot: number)
|
||||||
if slot < 1 or slot > HOTBAR_SIZE then
|
if slot < 1 or slot > HOTBAR_SIZE then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
local info = ClientState:GetSlotInfo(slot)
|
||||||
|
if not info then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
ClientState:SetSelectedSlot(slot)
|
||||||
self:setState({
|
self:setState({
|
||||||
selected = slot,
|
selected = slot,
|
||||||
})
|
})
|
||||||
local id = self.state.slots and self.state.slots[slot] or ""
|
local id = tostring(info.id)
|
||||||
local name = ""
|
local name = info.name or id
|
||||||
if id ~= "" and self.state.names then
|
|
||||||
name = self.state.names[id] or id
|
|
||||||
end
|
|
||||||
Util.StudioLog("[PLACE][CLIENT][SELECT]", "slot", slot, "id", id, "name", name)
|
Util.StudioLog("[PLACE][CLIENT][SELECT]", "slot", slot, "id", id, "name", name)
|
||||||
PlacementState:SetSelected(id, name)
|
PlacementState:SetSelected(id, name)
|
||||||
end
|
end
|
||||||
@@ -258,11 +261,11 @@ end
|
|||||||
|
|
||||||
function Hotbar:didMount()
|
function Hotbar:didMount()
|
||||||
self._connections = {
|
self._connections = {
|
||||||
blocksFolder.ChildAdded:Connect(self._updateSlots),
|
ClientState.Changed:Connect(self._syncFromClientState),
|
||||||
blocksFolder.ChildRemoved:Connect(self._updateSlots),
|
|
||||||
UIS.InputBegan:Connect(self._handleInput),
|
UIS.InputBegan:Connect(self._handleInput),
|
||||||
UIS.InputChanged:Connect(self._handleScroll),
|
UIS.InputChanged:Connect(self._handleScroll),
|
||||||
}
|
}
|
||||||
|
self._syncFromClientState()
|
||||||
self:_refreshViewports()
|
self:_refreshViewports()
|
||||||
-- initialize selection broadcast
|
-- initialize selection broadcast
|
||||||
local id = self.state.slots and self.state.slots[self.state.selected] or ""
|
local id = self.state.slots and self.state.slots[self.state.selected] or ""
|
||||||
|
|||||||
1
ThirdParty/Character-Realism
vendored
Submodule
1
ThirdParty/Character-Realism
vendored
Submodule
Submodule ThirdParty/Character-Realism added at 14021f7c4d
@@ -7,10 +7,15 @@ name = "evaera/cmdr"
|
|||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
dependencies = []
|
dependencies = []
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ivasmigins/replica"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ocbwoy3-development-studios/minecraft-roblox"
|
name = "ocbwoy3-development-studios/minecraft-roblox"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [["cmdr", "evaera/cmdr@1.12.0"], ["roact", "roblox/roact@1.4.4"]]
|
dependencies = [["cmdr", "evaera/cmdr@1.12.0"], ["replica", "ivasmigins/replica@0.1.0"], ["roact", "roblox/roact@1.4.4"]]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roblox/roact"
|
name = "roblox/roact"
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ realm = "shared"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cmdr = "evaera/cmdr@1.12.0"
|
cmdr = "evaera/cmdr@1.12.0"
|
||||||
roact = "roblox/roact@1.4.4"
|
roact = "roblox/roact@1.4.4"
|
||||||
|
replica = "ivasmigins/replica@0.1.0"
|
||||||
Reference in New Issue
Block a user