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:
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",
},
-- Remove fields not in template during data reconciliation
CleanExtraFields = true,
-- Batch processing settings
BatchDelay = 0.03, -- Seconds to wait before processing batch
BatchSize = 20, -- Auto-flush batch when this many operations are queued
MaxCacheSize = 1000, -- Maximum number of cached path keys
},
Client = {
-- How long to cache values (in seconds)
CacheDuration = 0.1,
-- How often to clean up expired cache entries (in seconds)
CacheCleanupInterval = 30,
-- Maximum number of cached values before cleanup
MaxCacheSize = 1000,
-- Maximum number of cached path strings before cleanup
MaxPathCacheSize = 1000,
},
}
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 | true | Remove fields not in template during reconciliation |
BatchDelay | 0.03 | Batch processing delay in seconds |
BatchSize | 20 | Operations to queue before auto-flush |
MaxCacheSize | 1000 | Maximum cached path keys |
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,
},
}
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
DataStore Versioning
Always increment DataStore.Name
when making breaking changes to your data structure to avoid data corruption.
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,
},
}
The Configuration module ensures consistent behavior across your game while allowing easy customization for different environments and performance requirements.
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
end
Client-side Safety
lua
-- Safe data access
local function getPlayerCoins()
local coins = PlayerState.Get("Coins")
return coins or 0 -- Fallback value
end
Next Steps
- API Reference - Complete function documentation
- Examples - Common usage patterns
- Best Practices - Recommended approaches