Skip to content

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

SettingDefaultDescription
Profile.Key"PlayerData"Unique identifier for ProfileStore data
Profile.TemplateDefaultDataData structure template (auto-populated)
DataStore.Name"PlayerData_0.01"DataStore name for versioning
DataStore.Scope"Production"Data scope ("Production" or "Testing")
CleanExtraFieldstrueRemove fields not in template during reconciliation
BatchDelay0.03Batch processing delay in seconds
BatchSize20Operations to queue before auto-flush
MaxCacheSize1000Maximum cached path keys

Client Configuration Options

SettingDefaultDescription
CacheDuration0.1Value cache duration in seconds
CacheCleanupInterval30Cache cleanup frequency in seconds
MaxCacheSize1000Maximum cached values
MaxPathCacheSize1000Maximum 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

PlayerState - High-Performance Roblox Data Management