Appearance
Setup
This guide walks you through setting up PlayerState in your ROBLOX game.
PlayerState V1
This is PlayerState V1 - The foundation is solid and production-ready, but I'm actively developing new features and improvements based on community feedback and real-world usage. Updates are frequent with bug fixes and enhancements.
Server Setup
CRITICAL: You MUST call PlayerState.Init()!
⚠️ ESSENTIAL: You MUST call PlayerState.Init(player) when each player joins or PlayerState will not work at all! This is the only required setup step that initializes ProfileStore data and Replica connections.
1. Initialize Players
Create a server script to handle player initialization:
lua
-- ServerScriptService.PlayerManager
local Players = game:GetService("Players")
local PlayerState = require(game.ReplicatedStorage.Libraries.PlayerState.PlayerStateServer)
-- ⚠️ CRITICAL: You MUST have this PlayerAdded connection!
Players.PlayerAdded:Connect(function(player)
local success = PlayerState.Init(player) -- ← ESSENTIAL - Don't skip this!
if success then
print(`[PlayerState] Successfully initialized data for {player.Name}`)
-- Optional: Set default values for new players
local coins = PlayerState.Get(player, "Coins")
if coins == 0 then -- New player
PlayerState.Set(player, "Coins", 100) -- Starting coins
PlayerState.SetPath(player, "Settings.MusicEnabled", true)
end
else
warn(`[PlayerState] Failed to initialize data for {player.Name}`)
-- Player will be automatically kicked
end
end)2. Data Auto-Save
PlayerState automatically saves data when:
- Player leaves the game
- Server shuts down
- ProfileStore session ends
No manual save calls are needed, but you can force save if required:
lua
-- Force save player data (usually not needed)
local success = PlayerState.SaveData(player)Client Setup
1. Wait for Data
Client data is automatically loaded when the player joins. The module handles waiting internally:
lua
-- LocalScript
local PlayerState = require(game.ReplicatedStorage.Libraries.PlayerState.PlayerStateClient)
-- Data will be automatically available
local coins = PlayerState.Get("Coins") -- Waits for data if not ready
print("Player coins:", coins)2. Listen for Changes
Set up event listeners for data changes:
lua
-- LocalScript - UI Updates
local PlayerState = require(game.ReplicatedStorage.Libraries.PlayerState.PlayerStateClient)
-- Update UI when coins change
local coinsConnection = PlayerState.OnChanged("Coins", function(newValue, oldValue)
updateCoinsDisplay(newValue)
end)
-- Listen to nested data
local likesConnection = PlayerState.OnChanged("Plot.Likes", function(newValue, oldValue)
updateLikesDisplay(newValue)
end)
-- Cleanup when done
coinsConnection:Disconnect()
likesConnection:Disconnect()DefaultData Configuration
Define your data schema in the DefaultData module:
lua
-- ReplicatedStorage.Libraries.PlayerState.DefaultData
return {
-- Basic Stats
Coins = 100,
Level = 1,
Experience = 0,
-- Collections
Inventory = {},
Achievements = {},
-- Nested Data
Settings = {
MusicEnabled = true,
SoundEnabled = true,
GraphicsQuality = "Medium",
},
-- Plot System
Plot = {
Likes = 0,
Size = 1,
Theme = "Default",
Buildings = {},
},
-- Game Stats
Stats = {
TotalPlayTime = 0,
GamesPlayed = 0,
HighScore = 0,
LastLogin = 0,
},
-- Purchases
Purchases = {
GamePasses = {},
DeveloperProducts = {},
}
}Environment Configuration
Development vs Production
Configure different environments using the PlayerStateConfig module:
lua
-- In PlayerStateConfig.lua
local Config = {
Server = {
Profile = {
Key = "PlayerData",
Template = DefaultData,
},
DataStore = {
Name = game.PlaceId == YOUR_DEV_PLACE_ID and "PlayerData_DEV" or "PlayerData_PROD",
Scope = game.PlaceId == YOUR_DEV_PLACE_ID and "Testing" or "Production",
},
},
}Testing Mode
For testing without affecting live data:
lua
-- In PlayerStateConfig.lua
local Config = {
Server = {
DataStore = {
Name = "PlayerData_TEST",
Scope = "Testing", -- Uses ProfileStore.Mock
},
},
}PlayerStateConfig Module
PlayerState now includes a centralized configuration module that allows you to customize system behavior without modifying the core implementation files.
Location
lua
-- ReplicatedStorage.Libraries.PlayerState.PlayerStateConfig
local Config = require(ReplicatedStorage.Libraries.PlayerState.PlayerStateConfig)Configuration Structure
The Configuration module separates settings for server and client environments, allowing you to customize PlayerState behavior without modifying core files:
lua
local Config = {
Server = {
Profile = {
-- Unique key for ProfileStore data identification
Key = "PlayerData",
-- Data template/structure (automatically pulled from DefaultData)
Template = DefaultData,
},
DataStore = {
-- DataStore name (change for different versions/environments)
Name = "PlayerData_0.01",
-- Scope: "Production" for live data, "Testing" for mock data
Scope = "Production",
},
-- If it will remove fields not in template during data reconciliation
CleanExtraFields = false,
-- How long to wait for player data to load before timing out (in seconds)
DataWaitTimeout = 10,
-- Batch processing settings
BatchDelay = 0.03, -- Seconds to wait before processing batch (lower = faster, higher = more efficient)
BatchSize = 20, -- Auto-flush batch when this many operations are queued
MaxCacheSize = 1000, -- Maximum number of cached path keys
-- ⚠️ Leaderboard configuration (REQUIRED for leaderboard functionality)
Leaderboard = {
Enabled = true, -- Must be true for leaderboards to work
-- DataStore name for leaderboard data
DataStoreName = "PlayerState_Leaderboards",
-- Which data fields should have leaderboards (auto-updated when values change)
TrackedStats = {
"Gems", -- Track gem count
"Coins", -- Track coin count
-- Add any numeric stats you want to track
-- Examples: "Level", "HighScore", "Plot.Likes", "Stats.TotalPlayTime"
},
-- How often to sync leaderboard data (in seconds) - minimum 60 recommended
SyncInterval = 60,
-- Update leaderboards immediately when players leave (recommended)
UpdateOnPlayerLeave = true,
},
},
Client = {
-- How long to cache values before checking for updates (in seconds)
CacheDuration = 0.1,
-- How often to clean up expired cache entries (in seconds)
CacheCleanupInterval = 30,
-- Maximum number of cached values before automatic cleanup
MaxCacheSize = 1000,
-- Maximum number of cached path strings before automatic cleanup
MaxPathCacheSize = 1000,
},
}Configuration Notes
- Modify these values in your
PlayerStateConfig.luafile to customize behavior - All settings have sensible defaults - only change what you need to customize
- Test in development before deploying configuration changes to production
- Monitor performance and adjust batch/cache settings based on your game's needs
Server Configuration Options
| Setting | Default | Description |
|---|---|---|
Profile.Key | "PlayerData" | Unique identifier for ProfileStore data |
Profile.Template | DefaultData | Data structure template (auto-populated) |
DataStore.Name | "PlayerData_0.01" | DataStore name for versioning |
DataStore.Scope | "Production" | Data scope ("Production" or "Testing") |
CleanExtraFields | false | Remove fields not in template during reconciliation |
DataWaitTimeout | 10 | Seconds to wait for player data before timeout |
BatchDelay | 0.03 | Batch processing delay in seconds |
BatchSize | 20 | Operations to queue before auto-flush |
MaxCacheSize | 1000 | Maximum cached path keys |
Leaderboard.Enabled | true | Enable/disable leaderboard functionality |
Leaderboard.DataStoreName | "PlayerState_Leaderboards" | DataStore for leaderboard data |
Leaderboard.TrackedStats | {"Coins", "Level", ...} | Stats to track on leaderboards |
Leaderboard.SyncInterval | 60 | Leaderboard sync frequency in seconds |
Leaderboard.UpdateOnPlayerLeave | true | Update leaderboards when players leave |
Detailed Configuration Explanations
Server Settings
Profile.Key
- Unique identifier for your game's ProfileStore data
- Use different keys for different games or major versions
- Should be consistent across all servers in your game
Profile.Template
- Automatically populated from your DefaultData module
- Defines the structure of player data
- Used for data reconciliation when loading profiles
DataStore.Name
- Roblox DataStore name for storing player data
- Increment this when making breaking data structure changes
- Use different names for development vs production environments
DataStore.Scope
"Production"for live player data"Testing"for development/mock data (uses ProfileStore.Mock)- Automatically switches to testing mode when scope is "Testing"
CleanExtraFields
false= Keep extra fields not in template (safer)true= Remove fields not in template during reconciliation- Set to
truefor cleaner data, but may lose custom fields
DataWaitTimeout
- Seconds to wait for player data to load before timing out
- Player gets kicked if data doesn't load within this time
- Increase for slower DataStore responses, decrease for faster failure
Batch Processing
BatchDelay: How long to wait before processing queued operations (0.01-0.1 recommended)BatchSize: Maximum operations to queue before auto-processing (10-50 recommended)- Lower delay = faster response, higher CPU usage
- Higher batch size = more efficient, potentially higher memory usage
MaxCacheSize
- Maximum number of cached path keys for performance
- Higher values = better performance, more memory usage
- Lower values = less memory, slightly slower path parsing
Leaderboard Settings
Leaderboard.Enabled
- Must be
truefor any leaderboard functionality to work - Functions return default values when disabled
- Set to
falseto temporarily disable leaderboards
Leaderboard.DataStoreName
- Base name for leaderboard DataStores
- Each tracked stat creates:
{DataStoreName}_{statName} - Example:
"Coins"becomes"PlayerState_Leaderboards_Coins"
Leaderboard.TrackedStats
- Array of data paths to track on leaderboards
- Only numeric values can be tracked
- Supports nested paths:
"Plot.Likes","Stats.TotalPlayTime" - Empty array disables leaderboard tracking
Leaderboard.SyncInterval
- How often to sync leaderboard data (seconds)
- Minimum recommended: 60 seconds (DataStore limits)
- Lower values = more frequent updates, higher DataStore usage
- Consider your game's scale and DataStore limits
Leaderboard.UpdateOnPlayerLeave
true= Update leaderboards when players leave (recommended)false= Only update during sync intervals- Immediate updates provide better user experience
Client Settings
CacheDuration
- How long to cache values before checking for updates (seconds)
- Lower values = more responsive, higher network usage
- Higher values = less responsive, lower network usage
CacheCleanupInterval
- How often to clean up expired cache entries (seconds)
- Lower values = more frequent cleanup, slightly higher CPU usage
- Higher values = less frequent cleanup, slightly higher memory usage
MaxCacheSize & MaxPathCacheSize
- Maximum cached items before cleanup
- Higher values = better performance, more memory usage
- Lower values = less memory, may cause more cache misses
Client Configuration Options
| Setting | Default | Description |
|---|---|---|
CacheDuration | 0.1 | Value cache duration in seconds |
CacheCleanupInterval | 30 | Cache cleanup frequency in seconds |
MaxCacheSize | 1000 | Maximum cached values |
MaxPathCacheSize | 1000 | Maximum cached path strings |
Customizing Configuration
To customize settings, modify the Configuration module values:
lua
-- PlayerStateConfig.lua
local Config = {
Server = {
DataStore = {
-- Use different DataStore names for different environments
Name = game.PlaceId == 123456789 and "PlayerData_DEV" or "PlayerData_PROD",
Scope = game.PlaceId == 123456789 and "Testing" or "Production",
},
-- Development: Faster batching for testing
BatchDelay = game.PlaceId == 123456789 and 0.01 or 0.03,
BatchSize = game.PlaceId == 123456789 and 5 or 20,
},
Client = {
-- Development: Shorter cache for faster testing
CacheDuration = game.PlaceId == 123456789 and 0.05 or 0.1,
},
}lua
-- PlayerStateConfig.lua
local Config = {
Server = {
-- High-performance game settings
BatchDelay = 0.02, -- Faster batching
BatchSize = 30, -- Larger batches
MaxCacheSize = 2000, -- More caching
},
Client = {
-- Optimize for memory-conscious environments
CacheDuration = 0.2, -- Longer cache duration
CacheCleanupInterval = 60, -- Less frequent cleanup
MaxCacheSize = 500, -- Smaller cache
MaxPathCacheSize = 500,
},
}lua
-- PlayerStateConfig.lua
local Config = {
Server = {
-- Conservative memory usage
BatchDelay = 0.05, -- Less frequent batching
BatchSize = 10, -- Smaller batches
MaxCacheSize = 500, -- Reduced caching
CleanExtraFields = true, -- Clean up unused data
},
Client = {
-- Minimal memory footprint
CacheDuration = 0.05, -- Short cache duration
CacheCleanupInterval = 15, -- Frequent cleanup
MaxCacheSize = 250, -- Small cache
MaxPathCacheSize = 250,
},
}lua
-- PlayerStateConfig.lua - For games with many concurrent players
local Config = {
Server = {
-- Optimize for high traffic
BatchDelay = 0.02, -- Faster batching for responsiveness
BatchSize = 50, -- Larger batches for efficiency
MaxCacheSize = 2000, -- More caching for performance
DataWaitTimeout = 15, -- Longer timeout for slower responses
Leaderboard = {
Enabled = true,
DataStoreName = "PlayerState_Leaderboards",
TrackedStats = {"Coins", "Level", "HighScore"},
SyncInterval = 120, -- Longer interval to reduce DataStore load
UpdateOnPlayerLeave = true,
},
},
Client = {
-- Balanced caching for high traffic
CacheDuration = 0.2, -- Longer cache to reduce network calls
CacheCleanupInterval = 60, -- Less frequent cleanup
MaxCacheSize = 2000, -- More caching for performance
MaxPathCacheSize = 2000,
},
}lua
-- PlayerStateConfig.lua - Development environment
local Config = {
Server = {
Profile = {
Key = "PlayerData_DEV",
},
DataStore = {
Name = "PlayerData_DEV",
Scope = "Testing", -- Use mock data for development
},
-- Development-friendly settings
BatchDelay = 0.01, -- Very fast batching for testing
BatchSize = 5, -- Small batches for immediate feedback
DataWaitTimeout = 5, -- Fail fast for development
Leaderboard = {
Enabled = true,
DataStoreName = "PlayerState_Leaderboards_DEV",
TrackedStats = {"Coins", "Level", "HighScore"},
SyncInterval = 30, -- Faster sync for development
UpdateOnPlayerLeave = true,
},
},
Client = {
-- Development settings for faster iteration
CacheDuration = 0.05, -- Short cache for testing changes
CacheCleanupInterval = 10, -- Frequent cleanup
MaxCacheSize = 500, -- Smaller cache for development
MaxPathCacheSize = 500,
},
}Configuration Tips
Performance Optimization
- Lower
BatchDelay: Faster response, higher CPU usage - Higher
BatchSize: More efficient batching, potentially higher memory usage - Larger cache sizes: Better performance, more memory usage
Leaderboard Optimization
- SyncInterval ≥ 60 seconds: Respect DataStore limits
- UpdateOnPlayerLeave = true: Immediate updates for better UX
- Limit TrackedStats: Only track stats you actually display
- Use nested paths:
"Stats.TotalPlayTime"instead of separate stats
Development Best Practices
- Use different DataStore names for dev vs production
- Set Scope = "Testing" for development with mock data
- Lower SyncInterval in development for faster testing
- Check console warnings for configuration issues
DataStore Versioning
Always increment DataStore.Name when making breaking changes to your data structure to avoid data corruption.
Leaderboard DataStore Limits
Roblox DataStore limits apply to leaderboards:
- 60 requests per minute per player
- Leaderboard updates count toward this limit
- Consider your game's scale when configuring SyncInterval
- Monitor DataStore usage in production
Advanced Configuration
lua
-- Example: Conditional configuration based on server region
local Config = {
Server = {
DataStore = {
Name = "PlayerData_" .. (game.JobId:sub(1,8) or "default"),
Scope = game.PrivateServerId and "Private" or "Production",
},
-- Adjust performance based on server load
BatchDelay = game.Players.MaxPlayers > 20 and 0.05 or 0.03,
BatchSize = game.Players.MaxPlayers > 20 and 30 or 20,
MaxCacheSize = game.Players.MaxPlayers > 20 and 1500 or 1000,
},
}Troubleshooting Configuration Issues
Common Configuration Problems
Problem: Leaderboard functions return empty arrays or nil
lua
-- ❌ This happens when leaderboard is disabled
local leaderboard = PlayerState.GetLeaderboard("Coins", 10) -- Returns {}Solution: Check your configuration:
lua
-- Make sure these are set correctly:
Leaderboard = {
Enabled = true, -- Must be true
TrackedStats = {"Coins"}, -- Must include the stat you're querying
}Problem: "Stat is not configured" warnings in console
[PlayerState] Stat "Experience" is not configured as a tracked leaderboard statSolution: Add the stat to TrackedStats:
lua
TrackedStats = {
"Coins",
"Experience", -- Add missing stats here
"Level",
}Problem: DataStore limit errors
DataStore request was rejected due to rate limitingSolution: Increase SyncInterval:
lua
Leaderboard = {
SyncInterval = 120, -- Increase from 60 to reduce DataStore calls
}Problem: Players getting kicked with "Failed to load data"
PlayerState: Timeout waiting for player's data to loadSolution: Increase DataWaitTimeout:
lua
DataWaitTimeout = 20, -- Increase from 10 for slower DataStore responsesProblem: Leaderboard not updating immediately
Player's leaderboard rank doesn't update after stat changesSolution: Enable UpdateOnPlayerLeave or use manual updates:
lua
-- Option 1: Enable immediate updates
UpdateOnPlayerLeave = true,
-- Option 2: Manual update when needed
PlayerState.UpdateLeaderboard(player, "Coins", newCoinValue)Debugging Configuration
Check your current configuration at runtime:
lua
-- Server Script - Debug current configuration
local Config = require(game.ReplicatedStorage.Libraries.PlayerState.PlayerStateConfig)
print("=== PlayerState Configuration Debug ===")
print("Leaderboard Enabled:", Config.Server.Leaderboard.Enabled)
print("Tracked Stats:", table.concat(Config.Server.Leaderboard.TrackedStats, ", "))
print("Sync Interval:", Config.Server.Leaderboard.SyncInterval)
print("DataStore Name:", Config.Server.Leaderboard.DataStoreName)
print("Batch Delay:", Config.Server.BatchDelay)
print("Batch Size:", Config.Server.BatchSize)Test leaderboard functionality:
lua
-- Server Script - Test leaderboard configuration
local function testLeaderboardConfig()
print("Testing leaderboard configuration...")
-- Test if leaderboard is enabled
local testData = PlayerState.GetLeaderboard("Coins", 1)
if #testData == 0 then
warn("Leaderboard not working - check configuration!")
return false
end
print("✅ Leaderboard configuration is working")
return true
endMonitor DataStore usage:
lua
-- Server Script - Monitor DataStore requests
local dataStoreRequests = 0
-- Hook into ProfileStore to count requests (if available)
-- This helps you stay within DataStore limits
game:BindToClose(function()
print(`Total DataStore requests this session: {dataStoreRequests}`)
end)The Configuration module ensures consistent behavior across your game while allowing easy customization for different environments and performance requirements.
Critical: DataStore Limits
Be aware of Roblox DataStore limits when configuring leaderboards:
- 60 requests per minute per player (including leaderboards)
- SyncInterval should be ≥ 60 seconds for optimal performance
- Leaderboard updates count toward your DataStore quota
- Consider your game's scale before lowering SyncInterval
Runtime Validation
PlayerState now includes runtime configuration validation:
- Leaderboard functions validate configuration at runtime
- Misconfigured settings trigger console warnings
- Functions return safe defaults when configuration is invalid
- Check console output for configuration issues during development
🏆 Leaderboard Configuration
Important: Leaderboard Config Required
You MUST configure the Leaderboard section in PlayerStateConfig for leaderboard functionality to work!
Without proper leaderboard configuration, all leaderboard functions will return nil or empty results.
Basic Leaderboard Setup
lua
-- PlayerStateConfig.lua
local Config = {
Server = {
Leaderboard = {
Enabled = true, -- Required: Enable leaderboards
DataStoreName = "PlayerState_Leaderboards", -- DataStore for leaderboard data
TrackedStats = { -- Required: Stats to track
"Coins", -- Track coin count
"Level", -- Track player level
"HighScore", -- Track high scores
"Plot.Likes", -- Track plot likes (nested path)
"Stats.TotalPlayTime", -- Track total playtime
},
SyncInterval = 60, -- How often to sync (seconds)
UpdateOnPlayerLeave = true, -- Update when player leaves
},
},
}Leaderboard Configuration Options
| Setting | Default | Description |
|---|---|---|
Enabled | true | Must be true for leaderboards to work |
DataStoreName | "PlayerState_Leaderboards" | DataStore for leaderboard data |
TrackedStats | {"Coins", "Level", ...} | Array of data paths to track |
SyncInterval | 60 | How often to sync leaderboard data (seconds) |
UpdateOnPlayerLeave | true | Update leaderboards when players leave |
TrackedStats Configuration
The TrackedStats array defines which data fields should be tracked on leaderboards:
lua
TrackedStats = {
-- Top-level stats
"Coins", -- Tracks player.Coins
"Level", -- Tracks player.Level
"HighScore", -- Tracks player.HighScore
-- Nested stats (using dot notation)
"Plot.Likes", -- Tracks player.Plot.Likes
"Stats.TotalPlayTime", -- Tracks player.Stats.TotalPlayTime
"Inventory.Count", -- Tracks player.Inventory.Count
-- Any numeric data path
"Achievements.Total", -- Custom achievement tracking
}Important Notes
- Only numeric values can be tracked on leaderboards
- Paths must exist in your DefaultData structure
- Nested paths are supported using dot notation
- Empty array disables leaderboard tracking
Leaderboard DataStore
Each tracked stat creates its own OrderedDataStore:
"Coins"→"PlayerState_Leaderboards_Coins""Plot.Likes"→"PlayerState_Leaderboards_Plot_Likes""Stats.TotalPlayTime"→"PlayerState_Leaderboards_Stats_TotalPlayTime"
DataStore Limits
Remember Roblox DataStore limits:
- 60 requests per minute per player
- Consider your game's scale when setting
SyncInterval
Automatic vs Manual Updates
Automatic Updates (recommended):
- Leaderboards update automatically when tracked data changes
- No additional code needed
- Happens during
SyncIntervalor when players leave
Manual Updates (for special cases):
lua
-- Force immediate leaderboard update
PlayerState.UpdateLeaderboard(player, "Coins", 5000)Testing Leaderboards
For testing without affecting live data:
lua
-- Testing configuration
Leaderboard = {
Enabled = true,
DataStoreName = "PlayerState_Leaderboards_TEST",
TrackedStats = {"Coins", "Level"},
SyncInterval = 30, -- Faster sync for testing
UpdateOnPlayerLeave = true,
}Runtime Validation
PlayerState now includes runtime validation for all leaderboard functions:
lua
-- ✅ This will work (Coins is in TrackedStats)
local leaderboard = PlayerState.GetLeaderboard("Coins", 10)
-- ❌ This will return empty array and warn (Level not in TrackedStats)
local levelBoard = PlayerState.GetLeaderboard("Level", 10) -- Returns {}, warns in console
-- ❌ This will return false and warn (leaderboard disabled)
local success = PlayerState.UpdateLeaderboard(player, "Coins", 100) -- Returns false, warns in consoleValidation Rules:
- Functions check
CONFIG.Leaderboard.Enabledat runtime - Functions validate stat names against
CONFIG.Leaderboard.TrackedStats - Invalid configurations return appropriate failure values and log warnings
- No crashes - graceful degradation with clear error messages
Common Patterns
1. Currency System
lua
-- Server: Award coins
PlayerState.Set(player, "Coins", PlayerState.Get(player, "Coins") + 50)
-- Or use SetValues for multiple updates
PlayerState.SetValues(player, {
Coins = PlayerState.Get(player, "Coins") + 50,
Experience = PlayerState.Get(player, "Experience") + 10
})2. Inventory Management
lua
-- Server: Add item to inventory
PlayerState.AddToArray(player, "Inventory", {
Id = "sword_001",
Name = "Iron Sword",
Rarity = "Common"
})
-- Remove item by index
PlayerState.RemoveFromArray(player, "Inventory", 1)3. Settings Management
lua
-- Server: Update nested settings
PlayerState.SetPath(player, "Settings.MusicEnabled", false)
PlayerState.SetInDict(player, "Settings", "GraphicsQuality", "High")
-- Client: Listen for settings changes
PlayerState.OnChanged("Settings.MusicEnabled", function(enabled)
toggleMusic(enabled)
end)Error Handling
Server-side Validation
lua
-- Validate before setting data
local function setPlayerCoins(player, amount)
if amount < 0 then
warn("Cannot set negative coins")
return false
end
if amount > 1000000 then
warn("Coins amount too high")
return false
end
PlayerState.Set(player, "Coins", amount)
return true
endClient-side Safety
lua
-- Safe data access
local function getPlayerCoins()
local coins = PlayerState.Get("Coins")
return coins or 0 -- Fallback value
endNext Steps
- API Reference - Complete function documentation
- Examples - Common usage patterns
- Best Practices - Recommended approaches