Skip to main content

PlayerStore

A PlayerStore wraps a regular Store to provide a more convenient API for working with Player data. It automatically converts Players to UserId keys and handles player kicks on data errors.

local playerStore = PlayerStore.create({
	name = "PlayerData",
	template = {
		coins = 0,
		items = {},
	},
	schema = function(data)
		return typeof(data.coins) == "number" and typeof(data.items) == "table",
		"Invalid data format"
	end,
})

-- Load data when player joins
Players.PlayerAdded:Connect(function(player)
	playerStore:loadAsync(player)
end)

-- Unload data when player leaves
Players.PlayerRemoving:Connect(function(player)
	playerStore:unloadAsync(player)
end)

Types

PlayerStoreConfig

interface PlayerStoreConfig {
namestring--

The name of the store

templateT--

The template data for new keys

schema(valueany) → (
boolean,
string?
)--

A function to validate data

migrationSteps{MigrationStep}?--

Optional migration steps

importLegacyData((keystring) → any?)?--

Optional function to import legacy data

changedCallbacks{(
keystring,
newDataT,
oldDataT?
) → ()}?--

Optional callbacks for data changes

logCallback((logMessageLogMessage) → ())?--

Optional callback for log messages

memoryStoreServiceMemoryStoreService?--

Optional MemoryStoreService instance for mocking

dataStoreServiceDataStoreService?--

Optional DataStoreService instance for mocking

}

Configuration for creating a new Store.

Functions

createPlayerStore

PlayerStore.createPlayerStore(
configPlayerStoreConfig<T>--

Configuration for the store

) → PlayerStore<T>

Creates a new PlayerStore with the given configuration. Configuration is similar to Store.createStore, but automatically adds player kick handling.

local playerStore = PlayerStore.create({
	name = "PlayerData",
	template = { coins = 0 },
	schema = function(data)
		return typeof(data.coins) == "number", "coins must be a number"
	end,

	-- Optional: Runs whenever data changes
	changedCallbacks = {
		function(key, newData, oldData)
			print(key, "changed from", oldData.coins, "to", newData.coins)
		end,
	},
})

Players will be automatically kicked with an error message if:

  • Their data fails to load
  • The DataStore lock is lost during their session

get

PlayerStore:get(playerPlayer) → Promise<T>--

Resolves with the player's data

Gets the current data for the given player.

playerStore:get(player):andThen(function(data)
	print(player.Name, "has", data.coins, "coins")
end)

Errors

TypeDescription
"Key not loaded"The player's data hasn't been loaded
"Store is closed"The store has been closed

getAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:getAsync(playerPlayer) → ()

Syntactic sugar for playerStore:get(player):expect().

See PlayerStore:get

load

PlayerStore:load(playerPlayer) → Promise--

Resolves when data is loaded

Loads data for the given player. Must be called before using other methods.

playerStore:load(player):andThen(function()
	print("Data loaded for", player.Name)
end)
CAUTION

If loading fails, the player will be kicked from the game.

Errors

TypeDescription
"Load already in progress"Another load is in progress for this player
"Store is closed"The store has been closed

loadAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:loadAsync(playerPlayer) → ()

Syntactic sugar for playerStore:load(player):expect().

See PlayerStore:load

unload

PlayerStore:unload(playerPlayer) → Promise<boolean>--

Resolves when the update is complete, with a boolean indicating success

Unloads data for the given player.

playerStore:unload(player):andThen(function()
	print("Data unloaded for", player.Name)
end)

Errors

TypeDescription
"Store is closed"The store has been closed

unloadAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:unloadAsync(playerPlayer) → ()

Syntactic sugar for playerStore:unload(player):expect().

See PlayerStore:unload

update

PlayerStore:update(
playerPlayer,
transformFunction(dataT) → boolean
) → Promise--

Resolves when the update is complete

Updates data for the given player using a transform function. The transform function must return true to commit changes, or false to abort.

playerStore:update(player, function(data)
	if data.coins < 100 then
		data.coins += 50
		return true -- Commit changes
	end
	return false -- Don't commit changes
end)

Errors

TypeDescription
"Key not loaded"The player's data hasn't been loaded
"Store is closed"The store has been closed
"Schema validation failed"The transformed data failed schema validation

updateAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:updateAsync(
playerPlayer,
transformFunction(dataT) → boolean
) → ()

Syntactic sugar for playerStore:update(player, transformFunction):expect().

See PlayerStore:update

updateImmutable

PlayerStore:updateImmutable(
playerPlayer,
transformFunction(dataT) → T | false
) → Promise--

Resolves when the update is complete

Updates data for the given player using a transform function that does not mutate the original data. The transform function must return the new data or false to abort.

playerStore:updateImmutable(player, function(data)
	if data.coins < 100 then
		return { coins = data.coins + 50 } -- Return new data to commit changes
	end
	return false -- Don't commit changes
end)

Errors

TypeDescription
"Key not loaded"The player's data hasn't been loaded
"Store is closed"The store has been closed
"Schema validation failed"The transformed data failed schema validation

updateImmutableAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:updateImmutableAsync(
playerPlayer,
transformFunction(dataT) → T | false
) → ()

Syntactic sugar for playerStore:updateImmutable(player, transformFunction):expect().

See PlayerStore:updateImmutable

tx

PlayerStore:tx(
players{Player},
transformFunction(state{[Player]T}) → boolean
) → Promise--

Resolves with true if the transaction was successful, or false if it was aborted. Rejects on error.

Performs a transaction across multiple players' data atomically. All players' data must be loaded first. Either all changes apply or none do.

playerStore:tx({player1, player2}, function(state)
	-- Transfer coins between players
	if state[player1].coins >= 100 then
		state[player1].coins -= 100
		state[player2].coins += 100
		return true -- Commit transaction
	end
	return false -- Abort transaction
end)

Errors

TypeDescription
"Key not loaded"One or more players' data hasn't been loaded
"Store is closed"The store has been closed
"Schema validation failed"The transformed data failed schema validation

txAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:txAsync(
players{Player},
transformFunction(state{[Player]T}) → boolean
) → ()

Syntactic sugar for playerStore:tx(players, transformFunction):expect().

See PlayerStore:tx

txImmutable

PlayerStore:txImmutable(
players{Player},
transformFunction(state{[Player]T}) → {[Player]T} | false
) → Promise--

Resolves with true if the transaction was successful, or false if it was aborted. Rejects on error.

Performs a transaction across multiple players' data atomically using immutable updates. All players' data must be loaded first. Either all changes apply or none do.

playerStore:txImmutable({player1, player2}, function(state)
	-- Transfer coins between players
	if state[player1].coins >= 100 then
		return {
			[player1] = { coins = state[player1].coins - 100 },
			[player2] = { coins = state[player2].coins + 100 },
		} -- Commit transaction with new data
	end
	return false -- Abort transaction
end)

Errors

TypeDescription
"Key not loaded"One or more players' data hasn't been loaded
"Store is closed"The store has been closed
"Schema validation failed"The transformed data failed schema validation

txImmutableAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:txImmutableAsync(
players{Player},
transformFunction(state{[Player]T}) → {[Player]T} | false
) → ()

Syntactic sugar for playerStore:txImmutable(players, transformFunction):expect().

See PlayerStore:txImmutable

save

PlayerStore:save(playerPlayer) → Promise--

Resolves when the save is complete

Forces an immediate save of the given player's data.

INFO

Data is automatically saved periodically, so manual saves are usually unnecessary.

Errors

TypeDescription
"Key not loaded"The player's data hasn't been loaded
"Store is closed"The store has been closed

saveAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:saveAsync(playerPlayer) → ()

Syntactic sugar for playerStore:save(player):expect().

See PlayerStore:save

close

PlayerStore:close() → Promise--

Resolves when the store is closed

Closes the store and unloads all active sessions. The store cannot be used after closing.

closeAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:closeAsync() → ()

Syntactic sugar for playerStore:close():expect().

See PlayerStore:close

peek

PlayerStore:peek(userIdnumber) → Promise<T>--

Resolves with the current data

Returns the current data for the given key without loading it into the store.

playerStore:peek(userId):andThen(function(data)
	print("Current coins:", data.coins)
end)

peekAsync

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
PlayerStore:peekAsync(userIdnumber) → ()

Syntactic sugar for playerStore:peek(userId):expect().

See PlayerStore:peek

Show raw api
{
    "functions": [
        {
            "name": "createPlayerStore",
            "desc": "Creates a new PlayerStore with the given configuration.\nConfiguration is similar to Store.createStore, but automatically adds player kick handling.\n\n```lua\nlocal playerStore = PlayerStore.create({\n\tname = \"PlayerData\",\n\ttemplate = { coins = 0 },\n\tschema = function(data)\n\t\treturn typeof(data.coins) == \"number\", \"coins must be a number\"\n\tend,\n\n\t-- Optional: Runs whenever data changes\n\tchangedCallbacks = {\n\t\tfunction(key, newData, oldData)\n\t\t\tprint(key, \"changed from\", oldData.coins, \"to\", newData.coins)\n\t\tend,\n\t},\n})\n```\n\nPlayers will be automatically kicked with an error message if:\n- Their data fails to load\n- The DataStore lock is lost during their session",
            "params": [
                {
                    "name": "config",
                    "desc": "Configuration for the store",
                    "lua_type": "PlayerStoreConfig<T>"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "PlayerStore<T>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 164,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "_kickPlayer",
            "desc": "Internal helper to kick players when data errors occur.",
            "params": [
                {
                    "name": "keyOrPlayer",
                    "desc": "",
                    "lua_type": "string | Player"
                },
                {
                    "name": "message",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [],
            "function_type": "method",
            "private": true,
            "source": {
                "line": 193,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "get",
            "desc": "Gets the current data for the given player.\n\n```lua\nplayerStore:get(player):andThen(function(data)\n\tprint(player.Name, \"has\", data.coins, \"coins\")\nend)\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves with the player's data",
                    "lua_type": "Promise<T>"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Key not loaded\"",
                    "desc": "The player's data hasn't been loaded"
                },
                {
                    "lua_type": "\"Store is closed\"",
                    "desc": "The store has been closed"
                }
            ],
            "source": {
                "line": 218,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "getAsync",
            "desc": "Syntactic sugar for `playerStore:get(player):expect()`.\n\nSee [PlayerStore:get]",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 229,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "load",
            "desc": "Loads data for the given player. Must be called before using other methods.\n\n```lua\nplayerStore:load(player):andThen(function()\n\tprint(\"Data loaded for\", player.Name)\nend)\n```\n\n:::caution\nIf loading fails, the player will be kicked from the game.\n:::",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves when data is loaded",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Load already in progress\"",
                    "desc": "Another load is in progress for this player"
                },
                {
                    "lua_type": "\"Store is closed\"",
                    "desc": "The store has been closed"
                }
            ],
            "source": {
                "line": 251,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "loadAsync",
            "desc": "Syntactic sugar for `playerStore:load(player):expect()`.\n\nSee [PlayerStore:load]",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 265,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "unload",
            "desc": "Unloads data for the given player.\n\n```lua\nplayerStore:unload(player):andThen(function()\n\tprint(\"Data unloaded for\", player.Name)\nend)\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves when the update is complete, with a boolean indicating success",
                    "lua_type": "Promise<boolean>"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Store is closed\"",
                    "desc": "The store has been closed"
                }
            ],
            "source": {
                "line": 282,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "unloadAsync",
            "desc": "Syntactic sugar for `playerStore:unload(player):expect()`.\n\nSee [PlayerStore:unload]",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 293,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "update",
            "desc": "Updates data for the given player using a transform function.\nThe transform function must return true to commit changes, or false to abort.\n\n```lua\nplayerStore:update(player, function(data)\n\tif data.coins < 100 then\n\t\tdata.coins += 50\n\t\treturn true -- Commit changes\n\tend\n\treturn false -- Don't commit changes\nend)\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                },
                {
                    "name": "transformFunction",
                    "desc": "",
                    "lua_type": "(data: T) -> boolean"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves when the update is complete",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Key not loaded\"",
                    "desc": "The player's data hasn't been loaded"
                },
                {
                    "lua_type": "\"Store is closed\"",
                    "desc": "The store has been closed"
                },
                {
                    "lua_type": "\"Schema validation failed\"",
                    "desc": "The transformed data failed schema validation"
                }
            ],
            "source": {
                "line": 317,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "updateAsync",
            "desc": "Syntactic sugar for `playerStore:update(player, transformFunction):expect()`.\n\nSee [PlayerStore:update]",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                },
                {
                    "name": "transformFunction",
                    "desc": "",
                    "lua_type": "(data: T) -> boolean"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 328,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "updateImmutable",
            "desc": "Updates data for the given player using a transform function that does not mutate the original data.\nThe transform function must return the new data or false to abort.\n\n```lua\nplayerStore:updateImmutable(player, function(data)\n\tif data.coins < 100 then\n\t\treturn { coins = data.coins + 50 } -- Return new data to commit changes\n\tend\n\treturn false -- Don't commit changes\nend)\n```",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                },
                {
                    "name": "transformFunction",
                    "desc": "",
                    "lua_type": "(data: T) -> T | false\n"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves when the update is complete",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Key not loaded\"",
                    "desc": "The player's data hasn't been loaded"
                },
                {
                    "lua_type": "\"Store is closed\"",
                    "desc": "The store has been closed"
                },
                {
                    "lua_type": "\"Schema validation failed\"",
                    "desc": "The transformed data failed schema validation"
                }
            ],
            "source": {
                "line": 351,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "updateImmutableAsync",
            "desc": "Syntactic sugar for `playerStore:updateImmutable(player, transformFunction):expect()`.\n\nSee [PlayerStore:updateImmutable]",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                },
                {
                    "name": "transformFunction",
                    "desc": "",
                    "lua_type": "(data: T) -> T | false"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 365,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "tx",
            "desc": "Performs a transaction across multiple players' data atomically.\nAll players' data must be loaded first. Either all changes apply or none do.\n\n```lua\nplayerStore:tx({player1, player2}, function(state)\n\t-- Transfer coins between players\n\tif state[player1].coins >= 100 then\n\t\tstate[player1].coins -= 100\n\t\tstate[player2].coins += 100\n\t\treturn true -- Commit transaction\n\tend\n\treturn false -- Abort transaction\nend)\n```",
            "params": [
                {
                    "name": "players",
                    "desc": "",
                    "lua_type": "{ Player }"
                },
                {
                    "name": "transformFunction",
                    "desc": "",
                    "lua_type": "(state: { [Player]: T }) -> boolean\n"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves with `true` if the transaction was successful, or `false` if it was aborted. Rejects on error.",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Key not loaded\"",
                    "desc": "One or more players' data hasn't been loaded"
                },
                {
                    "lua_type": "\"Store is closed\"",
                    "desc": "The store has been closed"
                },
                {
                    "lua_type": "\"Schema validation failed\"",
                    "desc": "The transformed data failed schema validation"
                }
            ],
            "source": {
                "line": 391,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "txAsync",
            "desc": "Syntactic sugar for `playerStore:tx(players, transformFunction):expect()`.\n\nSee [PlayerStore:tx]",
            "params": [
                {
                    "name": "players",
                    "desc": "",
                    "lua_type": "{ Player }"
                },
                {
                    "name": "transformFunction",
                    "desc": "",
                    "lua_type": "(state: { [Player]: T }) -> boolean"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 436,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "txImmutable",
            "desc": "Performs a transaction across multiple players' data atomically using immutable updates.\nAll players' data must be loaded first. Either all changes apply or none do.\n\n```lua\nplayerStore:txImmutable({player1, player2}, function(state)\n\t-- Transfer coins between players\n\tif state[player1].coins >= 100 then\n\t\treturn {\n\t\t\t[player1] = { coins = state[player1].coins - 100 },\n\t\t\t[player2] = { coins = state[player2].coins + 100 },\n\t\t} -- Commit transaction with new data\n\tend\n\treturn false -- Abort transaction\nend)\n```",
            "params": [
                {
                    "name": "players",
                    "desc": "",
                    "lua_type": "{ Player }"
                },
                {
                    "name": "transformFunction",
                    "desc": "",
                    "lua_type": "(state: { [Player]: T }) -> { [Player]: T } | false\n"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves with `true` if the transaction was successful, or `false` if it was aborted. Rejects on error.",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Key not loaded\"",
                    "desc": "One or more players' data hasn't been loaded"
                },
                {
                    "lua_type": "\"Store is closed\"",
                    "desc": "The store has been closed"
                },
                {
                    "lua_type": "\"Schema validation failed\"",
                    "desc": "The transformed data failed schema validation"
                }
            ],
            "source": {
                "line": 463,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "txImmutableAsync",
            "desc": "Syntactic sugar for `playerStore:txImmutable(players, transformFunction):expect()`.\n\nSee [PlayerStore:txImmutable]",
            "params": [
                {
                    "name": "players",
                    "desc": "",
                    "lua_type": "{ Player }"
                },
                {
                    "name": "transformFunction",
                    "desc": "",
                    "lua_type": "(state: { [Player]: T }) -> { [Player]: T } | false\n"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 508,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "save",
            "desc": "Forces an immediate save of the given player's data.\n\n:::info\nData is automatically saved periodically, so manual saves are usually unnecessary.\n:::",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves when the save is complete",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Key not loaded\"",
                    "desc": "The player's data hasn't been loaded"
                },
                {
                    "lua_type": "\"Store is closed\"",
                    "desc": "The store has been closed"
                }
            ],
            "source": {
                "line": 527,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "saveAsync",
            "desc": "Syntactic sugar for `playerStore:save(player):expect()`.\n\nSee [PlayerStore:save]",
            "params": [
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 538,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "close",
            "desc": "Closes the store and unloads all active sessions.\nThe store cannot be used after closing.",
            "params": [],
            "returns": [
                {
                    "desc": "Resolves when the store is closed",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 549,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "closeAsync",
            "desc": "Syntactic sugar for `playerStore:close():expect()`.\n\nSee [PlayerStore:close]",
            "params": [],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 559,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "peek",
            "desc": "Returns the current data for the given key without loading it into the store.\n\n```lua\nplayerStore:peek(userId):andThen(function(data)\n\tprint(\"Current coins:\", data.coins)\nend)\n```",
            "params": [
                {
                    "name": "userId",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves with the current data",
                    "lua_type": "Promise<T>"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 575,
                "path": "src/PlayerStore.luau"
            }
        },
        {
            "name": "peekAsync",
            "desc": "Syntactic sugar for `playerStore:peek(userId):expect()`.\n\nSee [PlayerStore:peek]",
            "params": [
                {
                    "name": "userId",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "method",
            "yields": true,
            "source": {
                "line": 586,
                "path": "src/PlayerStore.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "PlayerStoreConfig",
            "desc": "Configuration for creating a new Store.",
            "fields": [
                {
                    "name": "name",
                    "lua_type": "string",
                    "desc": "The name of the store"
                },
                {
                    "name": "template",
                    "lua_type": "T",
                    "desc": "The template data for new keys"
                },
                {
                    "name": "schema",
                    "lua_type": "(value: any) -> (boolean, string?)",
                    "desc": "A function to validate data"
                },
                {
                    "name": "migrationSteps",
                    "lua_type": "{ MigrationStep }?",
                    "desc": "Optional migration steps"
                },
                {
                    "name": "importLegacyData",
                    "lua_type": "((key: string) -> any?)?",
                    "desc": "Optional function to import legacy data"
                },
                {
                    "name": "changedCallbacks",
                    "lua_type": "{ (key: string, newData: T, oldData: T?) -> () }?",
                    "desc": "Optional callbacks for data changes"
                },
                {
                    "name": "logCallback",
                    "lua_type": "((logMessage: LogMessage) -> ())?",
                    "desc": "Optional callback for log messages"
                },
                {
                    "name": "memoryStoreService",
                    "lua_type": "MemoryStoreService?",
                    "desc": "Optional MemoryStoreService instance for mocking"
                },
                {
                    "name": "dataStoreService",
                    "lua_type": "DataStoreService?",
                    "desc": "Optional DataStoreService instance for mocking"
                }
            ],
            "source": {
                "line": 114,
                "path": "src/PlayerStore.luau"
            }
        }
    ],
    "name": "PlayerStore",
    "desc": "A PlayerStore wraps a regular Store to provide a more convenient API for working with Player data.\nIt automatically converts Players to UserId keys and handles player kicks on data errors.\n\n```lua\nlocal playerStore = PlayerStore.create({\n\tname = \"PlayerData\",\n\ttemplate = {\n\t\tcoins = 0,\n\t\titems = {},\n\t},\n\tschema = function(data)\n\t\treturn typeof(data.coins) == \"number\" and typeof(data.items) == \"table\",\n\t\t\"Invalid data format\"\n\tend,\n})\n\n-- Load data when player joins\nPlayers.PlayerAdded:Connect(function(player)\n\tplayerStore:loadAsync(player)\nend)\n\n-- Unload data when player leaves\nPlayers.PlayerRemoving:Connect(function(player)\n\tplayerStore:unloadAsync(player)\nend)\n```",
    "source": {
        "line": 31,
        "path": "src/PlayerStore.luau"
    }
}