Appearance
Client Functions
Client-side PlayerState API for data access and change listeners.
Client Only - READ-ONLY
Client scripts only. These functions are READ-ONLY - no data modification!
Need to change data? Create your own RemoteEvents with server validation.
Data Access Functions
Get()
Get(key) → any
Gets a top-level data value. Automatically waits for data to load with built-in caching for performance.
Parameters: key: string
Returns: any - The value, or nil if not found
Example
lua
local PlayerState = require(ReplicatedStorage.Libraries.PlayerState.PlayerStateClient)
local coins = PlayerState.Get("Coins")
local level = PlayerState.Get("Level")
print("Player has", coins, "coins and is level", level)Note
Automatically waits for server data to sync with built-in caching for performance
GetPath()
GetPath(path) → any
Gets a nested data value using dot notation. Automatically waits for data to load with optimized nested value caching.
Parameters: path: ValuePath
Returns: any - The value, or nil if not found
Example
lua
local likes = PlayerState.GetPath("Plot.Likes")
local musicEnabled = PlayerState.GetPath("Settings.MusicEnabled")
local highScore = PlayerState.GetPath("Stats.HighScore")
print("Plot has", likes, "likes")
print("Music is", musicEnabled and "enabled" or "disabled")Note
Handles nested data structures safely with optimized caching
GetAll()
GetAll() → PlayerData?
Gets all player data. Automatically waits for data to load.
Parameters: None
Returns: PlayerData? - Complete data table, or nil if not loaded
Example
lua
local allData = PlayerState.GetAll()
if allData then
print("Complete player data:", allData)
-- Iterate through top-level keys
for key, value in pairs(allData) do
print(key, "=", value)
end
endNote
Returns a reference to the actual data (do not modify!)
Read-Only Data
Don't modify the returned data! It's read-only on the client.
GetFromDict()
GetFromDict(dictPath, key) → any
Gets a value from a dictionary using a key with automatic type conversion and caching.
Parameters: dictPath: ValuePath, key: string | number
Returns: any - The value, or nil if not found
Example
lua
local PlayerState = require(ReplicatedStorage.Libraries.PlayerState.PlayerStateClient)
-- Get building data
local houseData = PlayerState.GetFromDict("Plot.Buildings", "House")
if houseData then
print("House level:", houseData.Level)
print("House material:", houseData.Material)
end
-- Get with numeric key (automatically converted to string)
local equippedItem = PlayerState.GetFromDict("Inventory.Equipped", 1)
-- Get setting value
local quality = PlayerState.GetFromDict("Settings", "GraphicsQuality")
print("Graphics quality:", quality or "Default")Note
Automatically converts number keys to strings for consistency
Status Functions
IsReady()
IsReady() → boolean
Checks if player data is ready and replica is active.
Parameters: None
Returns: boolean - True if data is ready, false otherwise
Example
lua
if PlayerState.IsReady() then
local coins = PlayerState.Get("Coins")
print("Player has", coins, "coins")
else
print("Data not ready yet...")
endNote
Optional check - data access functions already wait automatically
Cache Management Functions
ClearCache()
ClearCache() → void
Manually clears all internal caches. Useful for memory management or debugging.
Parameters: None
Returns: Nothing
Example
lua
-- Clear cache when switching players or for debugging
PlayerState.ClearCache()
-- Cache will rebuild automatically on next access
local coins = PlayerState.Get("Coins")Note
Manual cache management with automatic rebuild on next access
Change Listener Functions
OnChanged()
OnChanged(pathOrKey, callback) → ReplicaClient.Connection?
Listens for changes to a specific data path or key with enhanced change information.
Parameters: pathOrKey: string, callback: (newValue: any, oldValue: any, changeInfo: ChangeInfo | {string}?) -> ()
Returns: ReplicaClient.Connection? - Connection to disconnect the listener
lua
-- Listen to top-level changes
local coinsConnection = PlayerState.OnChanged("Coins", function(newValue, oldValue)
print("Coins changed from", oldValue, "to", newValue)
updateCoinsUI(newValue)
end)lua
-- Listen to nested changes
local likesConnection = PlayerState.OnChanged("Plot.Likes", function(newValue, oldValue, pathInfo)
print("Likes changed:", newValue)
if pathInfo then
print("Full path:", table.concat(pathInfo, "."))
end
updateLikesDisplay(newValue)
end)lua
-- Listen to ALL changes with detailed action info
local allChangesConnection = PlayerState.OnChanged(".", function(newValue, oldValue, changeInfo)
if changeInfo and changeInfo.action then
local pathString = table.concat(changeInfo.path, ".")
print(`{changeInfo.action}: {pathString} = {newValue} (was {oldValue})`)
-- Handle different action types
if changeInfo.action == "TableInsert" then
print("Item added at index:", changeInfo.index)
elseif changeInfo.action == "TableRemove" then
print("Item removed from index:", changeInfo.index)
end
end
end)Enhanced Change Information:
ChangeInfotype provides detailed action information- Support for
"Set","SetValues","TableInsert","TableRemove"actions - Index information for array operations
- Full path information for all changes
Note
Callbacks fire immediately when data changes with automatic cache invalidation
Managing Connections
lua
-- Store connections for cleanup
local connections = {}
connections.coins = PlayerState.OnChanged("Coins", updateCoinsUI)
connections.level = PlayerState.OnChanged("Level", updateLevelUI)
-- NEW: Global listener with action handling
connections.all = PlayerState.OnChanged(".", function(newValue, oldValue, info)
if info and info.action == "TableInsert" then
print("Array item added!")
end
end)
-- Cleanup when done
for name, connection in pairs(connections) do
if connection then
connection:Disconnect()
end
endAdvanced Functions
GetReplica()
GetReplica() → ReplicaInstance?
Gets the raw Replica instance for advanced operations.
Parameters: None
Returns: ReplicaInstance? - Replica instance, or nil if not loaded
Example
lua
local replica = PlayerState.GetReplica()
if replica then
-- Access raw Replica API
print("Replica data:", replica.Data)
print("Replica tags:", replica.Tags)
-- Advanced change listener
replica:OnChange(function(action, path, param1, param2)
print("Raw change:", action, table.concat(path, "."))
end)
endNote
Provides access to the underlying Replica object with enhanced validation
Usage Examples
UI Updates with Enhanced Change Handling
Example
lua
local PlayerState = require(ReplicatedStorage.Libraries.PlayerState.PlayerStateClient)
-- UI Elements
local coinsLabel = gui.CoinsLabel
local levelLabel = gui.LevelLabel
local experienceBar = gui.ExperienceBar
-- Initial UI setup (data access automatically waits)
local function updateUI()
coinsLabel.Text = tostring(PlayerState.Get("Coins") or 0)
levelLabel.Text = "Level " .. tostring(PlayerState.Get("Level") or 1)
local exp = PlayerState.Get("Experience") or 0
local maxExp = (PlayerState.Get("Level") or 1) * 100
experienceBar.Size = UDim2.new(exp / maxExp, 0, 1, 0)
end
-- Listen for changes with enhanced info
local connections = {}
connections.coins = PlayerState.OnChanged("Coins", function(newValue, oldValue, info)
coinsLabel.Text = tostring(newValue or 0)
-- Show gain/loss animation
if oldValue and newValue > oldValue then
showGainAnimation(newValue - oldValue)
end
end)
connections.level = PlayerState.OnChanged("Level", function(newValue, oldValue, info)
levelLabel.Text = "Level " .. tostring(newValue or 1)
updateUI() -- Recalculate experience bar
if oldValue and newValue > oldValue then
showLevelUpEffect()
end
end)
-- NEW: Global change listener for debugging
connections.debug = PlayerState.OnChanged(".", function(newValue, oldValue, info)
if info and info.action then
print(`[DEBUG] {info.action} at {table.concat(info.path, ".")}`)
end
end)
-- Initial update
updateUI()
-- Enhanced cleanup function
local function cleanup()
for name, connection in pairs(connections) do
if connection and typeof(connection) == "table" and connection.Disconnect then
connection:Disconnect()
end
end
connections = {}
endSettings Management
Example
lua
-- Settings UI manager with caching
local SettingsManager = {}
local PlayerState = require(ReplicatedStorage.Libraries.PlayerState.PlayerStateClient)
local SoundService = game:GetService("SoundService")
function SettingsManager.init()
-- Apply initial settings (automatically waits for data)
local musicEnabled = PlayerState.GetPath("Settings.MusicEnabled")
local soundEnabled = PlayerState.GetPath("Settings.SoundEnabled")
SoundService.AmbientReverb = musicEnabled and Enum.ReverbType.City or Enum.ReverbType.NoReverb
SoundService.Volume = soundEnabled and 1 or 0
-- Listen for changes
PlayerState.OnChanged("Settings.MusicEnabled", function(enabled)
SoundService.AmbientReverb = enabled and Enum.ReverbType.City or Enum.ReverbType.NoReverb
end)
PlayerState.OnChanged("Settings.SoundEnabled", function(enabled)
SoundService.Volume = enabled and 1 or 0
end)
end
return SettingsManagerInventory Display with Array Change Handling
Example
lua
-- Inventory UI manager with enhanced change detection
local InventoryUI = {}
local PlayerState = require(ReplicatedStorage.Libraries.PlayerState.PlayerStateClient)
-- UI elements
local inventoryFrame = gui.InventoryFrame
local itemTemplate = inventoryFrame.ItemTemplate
function InventoryUI.updateDisplay()
-- Clear existing items
for _, child in pairs(inventoryFrame:GetChildren()) do
if child ~= itemTemplate and child:IsA("Frame") then
child:Destroy()
end
end
-- Get inventory data
local inventory = PlayerState.GetPath("Inventory") or {}
-- Create UI elements for each item
for index, item in ipairs(inventory) do
local itemFrame = itemTemplate:Clone()
itemFrame.Name = "Item" .. index
itemFrame.Visible = true
itemFrame.Parent = inventoryFrame
-- Update item display
itemFrame.ItemName.Text = item.Name or "Unknown"
itemFrame.Rarity.Text = item.Rarity or "Common"
itemFrame.LayoutOrder = index
end
end
function InventoryUI.init()
-- Initial display (automatically waits for data)
InventoryUI.updateDisplay()
-- NEW: Enhanced inventory change listener
PlayerState.OnChanged("Inventory", function(newValue, oldValue, info)
InventoryUI.updateDisplay()
-- Handle specific actions
if info and info.action == "TableInsert" then
showItemAddedEffect(info.index)
elseif info and info.action == "TableRemove" then
showItemRemovedEffect(info.index)
end
end)
end
return InventoryUIError Handling
Safe Data Access with Readiness Checks
Example
lua
-- Safe data access (automatically waits for data)
local function getPlayerData()
return {
coins = PlayerState.Get("Coins") or 0,
level = PlayerState.Get("Level") or 1,
likes = PlayerState.GetPath("Plot.Likes") or 0
}
end
-- Safe nested access with validation
local function getInventoryItem(index)
local inventory = PlayerState.GetPath("Inventory")
if not inventory or typeof(inventory) ~= "table" then
return nil
end
return inventory[index]
end
-- Check if specific data exists
local function hasValidInventory()
local inventory = PlayerState.GetPath("Inventory")
return inventory and typeof(inventory) == "table" and #inventory > 0
endEnhanced Connection Management
Example
lua
-- Advanced connection management system
local ConnectionManager = {}
local activeConnections = {}
function ConnectionManager.add(name, connection)
-- Cleanup existing connection
if activeConnections[name] then
if typeof(activeConnections[name]) == "table" and activeConnections[name].Disconnect then
activeConnections[name]:Disconnect()
end
end
activeConnections[name] = connection
end
function ConnectionManager.remove(name)
local connection = activeConnections[name]
if connection and typeof(connection) == "table" and connection.Disconnect then
connection:Disconnect()
end
activeConnections[name] = nil
end
function ConnectionManager.cleanup()
for name, connection in pairs(activeConnections) do
if connection and typeof(connection) == "table" and connection.Disconnect then
connection:Disconnect()
end
end
activeConnections = {}
end
-- Usage with enhanced error handling
ConnectionManager.add("coins", PlayerState.OnChanged("Coins", function(newValue, oldValue, info)
if typeof(newValue) == "number" then
updateCoinsDisplay(newValue)
end
else
warn("Invalid coins value received:", newValue)
end
end))
-- Cleanup when done
ConnectionManager.cleanup()Performance Tips
Caching & Optimization
Automatic Caching: Frequently accessed values are cached automatically for performance Cache Management: Use ClearCache() only when necessary (debugging, memory concerns) Readiness Checks: Use IsReady() before bulk operations to avoid unnecessary waits Specific Listeners: Use specific paths instead of global "." listeners when possible Connection Cleanup: Always disconnect listeners to prevent memory leaks
Best Practices
Fallbacks: Always provide fallbacks when accessing data that might not exist Auto-Waiting: Data access automatically waits - no manual waiting required Conditional Logic: Use IsReady() only for conditional logic or optimization Listener Management: Disconnect listeners when UI elements are destroyed Path Specificity: Use specific paths instead of listening to all changes Action Handling: Handle different action types in global listeners for better UX Read-Only Data: Don't modify returned data - it's a reference to actual data