Skip to content

Types

PlayerState uses TypeScript-style type annotations for better code safety and IntelliSense support.

Core Data Types

PlayerData

The main data structure for player information, based on your DefaultData module.

lua
export type PlayerData = typeof(DefaultData)

Example DefaultData structure:

lua
{
    Coins = 0,
    Level = 1,
    Experience = 0,
    Inventory = {},
    Settings = {
        MusicEnabled = true,
        SoundEnabled = true,
    },
    Plot = {
        Likes = 0,
        Size = 1,
        Buildings = {},
    },
    Stats = {
        TotalPlayTime = 0,
        GamesPlayed = 0,
        HighScore = 0,
    },
    Achievements = {},
    
    -- NEW: Leaderstats configuration
    leaderstats = {
        ["💰 Coins"] = "Coins",
        ["⭐ Level"] = "Level",
        ["👍 Likes"] = "Plot.Likes"
    }
}

Path Types

ValuePath

String type for dot-separated data paths.

lua
export type ValuePath = string

Examples:

lua
"Coins"              -- Top-level access
"Plot.Likes"         -- Nested access
"Settings.MusicEnabled"  -- Deep nesting
"Stats.HighScore"    -- Any depth
"Plot.Buildings.House.Level"  -- Complex nesting

ArrayPath

String type for paths to array/list data structures.

lua
export type ArrayPath = string

Examples:

lua
"Inventory"          -- Top-level array
"Plot.Buildings"     -- Nested array
"Achievements"       -- List of achievements
"Stats.RecentScores" -- Nested score array

DictPath

String type for paths to dictionary/table data structures.

lua
export type DictPath = string

Examples:

lua
"Settings"           -- Settings dictionary
"Plot.Buildings"     -- Building configuration
"Stats"              -- Player statistics
"Inventory.Equipped" -- Equipment slots

Batch Operation Types

BatchOperation

Type for individual batch operations used in BatchSetValues().

lua
export type BatchOperation = {
    path: ValuePath,
    value: any,
}

Example:

lua
local operations: {BatchOperation} = {
    {path = "Coins", value = 1000},
    {path = "Level", value = 5},
    {path = "Plot.Likes", value = 50},
    {path = "Settings.MusicEnabled", value = true}
}

Change Information Types

ChangeInfo

Enhanced type for change information in client listeners.

lua
export type ChangeInfo = {
    action: "Set" | "SetValues" | "TableInsert" | "TableRemove",
    path: {string},
    index: number?,
}

Action Types:

  • "Set": Single value change
  • "SetValues": Multiple values changed
  • "TableInsert": Item added to array
  • "TableRemove": Item removed from array

Properties:

  • action: The type of change that occurred
  • path: Array of path segments that changed
  • index: Optional index for array operations

Example Usage:

lua
PlayerState.OnChanged(".", function(newValue, oldValue, info)
    if info and info.action == "TableInsert" then
        print(`Item added at index {info.index} to {table.concat(info.path, ".")}`)
    end
end)

Server Interface Types

PlayerStateServer

The enhanced server module interface with all available functions.

lua
export type PlayerStateServer = {
    -- Initialization
    Init: (player: Player, existingData: PlayerData?) -> boolean,
    
    -- Status checking
    IsPlayerDataReady: (player: Player) -> boolean,
    
    -- Basic operations
    Set: (player: Player, key: string, value: any) -> boolean,
    Get: (player: Player, key: string) -> any,
    
    -- Path operations
    SetPath: (player: Player, path: ValuePath, value: any) -> boolean,
    GetPath: (player: Player, path: ValuePath) -> any,
    
    -- Batch operations
    SetValues: (player: Player, values: {[string]: any}) -> boolean,
    BatchSetValues: (player: Player, operations: {BatchOperation}) -> boolean,
    FlushBatch: (player: Player) -> boolean,
    
    -- Array operations
    AddToArray: (player: Player, arrayPath: ArrayPath, item: any) -> boolean,
    RemoveFromArray: (player: Player, arrayPath: ArrayPath, index: number) -> boolean,
    UpdateArrayItem: (player: Player, arrayPath: ArrayPath, index: number, newItem: any) -> boolean,
    
    -- Dictionary operations
    SetInDict: (player: Player, dictPath: DictPath, key: string, value: any) -> boolean,
    GetFromDict: (player: Player, dictPath: DictPath, key: string | number) -> any,
    RemoveFromDict: (player: Player, dictPath: DictPath, key: string) -> boolean,
    
    -- Cache management
    ClearPathCache: () -> (),
    
    -- Utility functions
    GetReplica: (player: Player) -> ReplicaInstance?,
    GetProfile: (player: Player) -> ProfileStoreProfile?,
    GetAll: (player: Player) -> PlayerData?,
    SaveData: (player: Player) -> boolean,
}

ProfileStoreProfile

Enhanced ProfileStore profile interface for data persistence.

lua
export type ProfileStoreProfile = {
    Data: PlayerData,
    OnSessionEnd: RBXScriptSignal,
    AddUserId: (self: ProfileStoreProfile, userId: number) -> (),
    Reconcile: (self: ProfileStoreProfile) -> (),
    EndSession: (self: ProfileStoreProfile) -> (),
    IsActive: (self: ProfileStoreProfile) -> boolean,
}

Key Properties:

  • Data: The actual player data
  • OnSessionEnd: Event fired when profile session ends
  • AddUserId(): Associates user ID with profile
  • Reconcile(): Merges template changes with existing data
  • EndSession(): Manually end the profile session
  • IsActive(): Check if profile is still active

Notes:

  • NEW: Enhanced validation for active status
  • NEW: Better session management
  • NEW: Automatic cleanup on session end

Client Interface Types

PlayerStateClient

The enhanced client module interface with all available functions.

lua
export type PlayerStateClient = {
    -- Data access
    Get: (key: string) -> any,
    GetPath: (path: ValuePath) -> any,
    GetFromDict: (dictPath: ValuePath, key: string | number) -> any,
    GetAll: () -> PlayerData?,
    
    -- Status checking
    IsReady: () -> boolean,
    
    -- Cache management
    ClearCache: () -> (),
    
    -- Change listeners
    OnChanged: (pathOrKey: string, callback: (newValue: any, oldValue: any, changeInfo: ChangeInfo | {string}?) -> ()) -> ReplicaClient.Connection?,
    
    -- Advanced access
    GetReplica: () -> ReplicaInstance?,
}

Key Changes:

  • NEW: IsReady() for status checking
  • NEW: ClearCache() for manual cache management
  • NEW: Enhanced OnChanged() with ChangeInfo support
  • NEW: Better error handling and validation

Enhanced Change Callback

Type for change listener callback functions with enhanced information.

lua
type ChangeCallback = (newValue: any, oldValue: any, changeInfo: ChangeInfo | {string}?) -> ()

Parameters:

  • newValue: The new value after the change
  • oldValue: The previous value before the change
  • changeInfo: Enhanced change information with action details OR simple path array

Example with Action Handling:

lua
local connection = PlayerState.OnChanged("Inventory", function(newValue, oldValue, info)
    if info and typeof(info) == "table" and info.action then
        -- Enhanced change info
        if info.action == "TableInsert" then
            print(`Item added at index {info.index}`)
        elseif info.action == "TableRemove" then
            print(`Item removed from index {info.index}`)
        end
    else
        -- Simple path info
        print("Inventory changed")
    end
end)

Replica System Types

ReplicaInstance

The Replica object used for real-time synchronization.

lua
export type ReplicaInstance = ReplicaServer.Replica -- or ReplicaClient.Replica

Common Properties:

  • Data: The synchronized data
  • Tags: Metadata tags (UserId, PlayerName, etc.)
  • OnChange: Advanced change listener
  • Subscribe/Unsubscribe: Client subscription management
  • NEW: Enhanced validation methods
  • NEW: Better error handling

ReplicaConnection

Connection object returned by change listeners.

lua
export type ReplicaConnection = ReplicaClient.Connection

Methods:

  • Disconnect(): Remove the listener and clean up

Notes:

  • NEW: Enhanced connection validation
  • NEW: Automatic cleanup on disconnect
  • NEW: Better memory management

Configuration Types

Enhanced Profile Configuration

lua
type ProfileConfig = {
    Key: string,        -- Base key for ProfileStore
    Template: PlayerData, -- Default data template
    GlobalUpdates: boolean, -- Enable global updates
}

Enhanced DataStore Configuration

lua
type DataStoreConfig = {
    Name: string,       -- DataStore name
    Scope: "Production" | "Testing", -- Environment scope
}

Complete Configuration with New Options

lua
type PlayerStateConfig = {
    Profile: ProfileConfig,
    DataStore: DataStoreConfig,
    CleanExtraFields: boolean,  -- NEW: Clean unused fields
    BatchDelay: number,         -- NEW: Batch processing delay
    BatchSize: number,          -- NEW: Auto-flush batch size
    MaxCacheSize: number,       -- NEW: Maximum cache entries
}

Default Configuration:

lua
-- PlayerStateConfig.lua
local Config = {
    Server = {
        Profile = {
            Key = "PlayerData",
            Template = DefaultData,
        },
        DataStore = {
            Name = "PlayerData_ALPHA_0.02",
            Scope = "Production",
        },
        CleanExtraFields = true,
        BatchDelay = 0.03,      -- 30ms batch delay
        BatchSize = 20,         -- Auto-flush at 20 operations
        MaxCacheSize = 1000,    -- Maximum cache entries
    },
    Client = {
        CacheDuration = 0.1,
        MaxCacheSize = 1000,
    },
}

Leaderstats Types

LeaderstatsConfig

Configuration type for automatic leaderstats integration.

lua
export type LeaderstatsConfig = {
    [string]: ValuePath  -- Display name -> data path
}

Example:

lua
local leaderstatsConfig: LeaderstatsConfig = {
    ["💰 Coins"] = "Coins",
    ["⭐ Level"] = "Level",
    ["👍 Likes"] = "Plot.Likes",
    ["🏆 High Score"] = "Stats.HighScore"
}

Notes:

  • Keys are the display names shown in leaderstats
  • Values are paths to the actual data
  • Supports nested paths with dot notation
  • Numbers automatically use IntValue, others use StringValue

Type-Safe Usage Examples

Server Type Safety

lua
local PlayerState: PlayerStateServer = require(...)

-- Type-safe function with proper return types
local function awardCoins(player: Player, amount: number, source: string): boolean
    if not PlayerState.IsPlayerDataReady(player) then
        warn(`Cannot award coins to {player.Name} - data not ready`)
        return false
    end
    
    local currentCoins: number = PlayerState.Get(player, "Coins") or 0
    
    if amount <= 0 then
        warn(`Invalid coin amount: {amount}`)
        return false
    end
    
    local success = PlayerState.Set(player, "Coins", currentCoins + amount)
    if success then
        print(`Awarded {amount} coins to {player.Name} from {source}`)
    end
    
    return success
end

-- Type-safe batch operations
local function levelUpPlayer(player: Player): boolean
    local operations: {BatchOperation} = {
        {path = "Level", value = (PlayerState.Get(player, "Level") or 1) + 1},
        {path = "Experience", value = 0},
        {path = "Coins", value = (PlayerState.Get(player, "Coins") or 0) + 500}
    }
    
    return PlayerState.BatchSetValues(player, operations)
end

-- Type-safe data retrieval
local function getPlayerStats(player: Player): {Level: number, Experience: number, Coins: number}?
    if not PlayerState.IsPlayerDataReady(player) then
        return nil
    end
    
    local allData: PlayerData? = PlayerState.GetAll(player)
    
    if not allData then
        return nil
    end
    
    return {
        Level = allData.Level or 1,
        Experience = allData.Experience or 0,
        Coins = allData.Coins or 0
    }
end

Client Type Safety

lua
local PlayerState: PlayerStateClient = require(...)

-- Type-safe change listeners with enhanced info
local function setupCoinDisplay(label: TextLabel): ReplicaConnection?
    return PlayerState.OnChanged("Coins", function(newValue: any, oldValue: any, info: ChangeInfo | {string}?)
        local coins: number = newValue or 0
        label.Text = tostring(coins)
        
        -- Handle enhanced change info
        if info and typeof(info) == "table" and info.action then
            if oldValue and coins > oldValue then
                showCoinGainEffect(coins - oldValue)
            end
        end
    end)
end

-- Type-safe global change listener
local function setupGlobalListener(): ReplicaConnection?
    return PlayerState.OnChanged(".", function(newValue: any, oldValue: any, info: ChangeInfo | {string}?)
        if info and typeof(info) == "table" and info.action then
            local changeInfo = info :: ChangeInfo
            local pathString = table.concat(changeInfo.path, ".")
            
            if changeInfo.action == "TableInsert" then
                print(`Item added to {pathString} at index {changeInfo.index}`)
            elseif changeInfo.action == "TableRemove" then
                print(`Item removed from {pathString} at index {changeInfo.index}`)
            end
        end
    end)
end

-- Type-safe data access with readiness check
local function getInventoryCount(): number?
    if not PlayerState.IsReady() then
        return nil
    end
    
    local inventory: {any} = PlayerState.GetPath("Inventory") or {}
    return #inventory
end

Custom Type Extensions

Custom Item Types

You can extend these types for your specific use case:

lua
-- Custom item type for inventory
export type InventoryItem = {
    Id: string,
    Name: string,
    Rarity: "Common" | "Rare" | "Epic" | "Legendary",
    Level: number,
    Attributes: {[string]: any}?,
    AcquiredTime: number?,
    UpgradedTime: number?,
}

-- Custom plot building type
export type PlotBuilding = {
    Type: string,
    Level: number,
    Position: Vector3,
    Rotation: number,
    Upgrades: {string},
    BuiltTime: number,
    Health: number,
}

-- Extended player data with custom types
export type ExtendedPlayerData = PlayerData & {
    Inventory: {InventoryItem},
    Plot: {
        Likes: number,
        Size: number,
        Buildings: {[string]: PlotBuilding},
    },
    Stats: {
        TotalPlayTime: number,
        GamesPlayed: number,
        HighScore: number,
        RecentScores: {number},
    }
}

-- Batch operation with custom validation
export type ValidatedBatchOperation = BatchOperation & {
    validate: () -> boolean,
    description: string,
}

Type Guards and Validation

Runtime Type Checking

Utility functions for runtime type checking:

lua
-- Check if value is a valid inventory item
local function isInventoryItem(value: any): boolean
    return typeof(value) == "table" 
        and typeof(value.Id) == "string"
        and typeof(value.Name) == "string"
        and typeof(value.Rarity) == "string"
        and typeof(value.Level) == "number"
end

-- Check if change info is enhanced
local function isChangeInfo(value: any): boolean
    return typeof(value) == "table"
        and typeof(value.action) == "string"
        and typeof(value.path) == "table"
end

-- Type-safe array access
local function getInventoryItem(index: number): InventoryItem?
    if not PlayerState.IsReady() then
        return nil
    end
    
    local inventory = PlayerState.GetPath("Inventory")
    
    if typeof(inventory) ~= "table" or not inventory[index] then
        return nil
    end
    
    local item = inventory[index]
    return isInventoryItem(item) and item or nil
end

-- Validate batch operations
local function validateBatchOperations(operations: {BatchOperation}): boolean
    for _, operation in operations do
        if typeof(operation.path) ~= "string" or operation.value == nil then
            return false
        end
        
        -- Check for leaderstats paths
        if operation.path == "leaderstats" or string.sub(operation.path, 1, 12) == "leaderstats." then
            warn(`Invalid batch operation: cannot modify leaderstats path {operation.path}`)
            return false
        end
    end
    
    return true
end

Performance Type Considerations

Caching Types

lua
-- Cache entry types
type CacheEntry<T> = {
    value: T,
    timestamp: number,
}

type PathCache = {[string]: {string}}
type ValueCache<T> = {[string]: CacheEntry<T>}

Connection Management Types

lua
-- Connection manager types
type ConnectionManager = {
    connections: {[string]: ReplicaConnection},
    add: (name: string, connection: ReplicaConnection) -> (),
    remove: (name: string) -> (),
    cleanup: () -> (),
}

These enhanced types provide better IntelliSense support, catch potential errors at development time, and make the codebase more maintainable with clear interfaces and expectations.

PlayerState - High-Performance Roblox Data Management