-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
177 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,5 @@ nav: | |
- instances-api.md | ||
- player-api.md | ||
- avatar-api.md | ||
- lua-behaviour.md | ||
- lua-behaviour.md | ||
- storage.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Storage | ||
|
||
The `Storage` global provides access to the script's associated Persistence object. The Persistence object is where your script can store simple forms of data on a user's computer. | ||
|
||
## Bins | ||
|
||
We provide two "storage bins:" The `Public` bin and the `Private` bin. Each bin is a property of the Storage global and are almost identical in terms of API. Each bin has a set amount of data that they can store, as well, to prevent malicious scripts from filling up the user's hard drive. | ||
|
||
### Public | ||
|
||
The Public bin is accessible as `Storage.Public`. This bin's corresponding file on disk is user-editable, as it is stored as clear-text JSON. This file is intended to be used for things the user may need to edit, such as configuration or tuning values. | ||
|
||
#### Properties | ||
|
||
| Name | Type | Notes | | ||
| --- | --- | --- | | ||
| `BytesAllowed` | `int` | <p>How many bytes are allowed for this storage bin, in total. Currently set to `4194304`, which corresponds to 4MiB.</p><p>Does not include data outside of your control that we add during the serialization process, such as encryption stuff, headers, section data, etc.</p> | | ||
| `CurrentSize` | `int` | <p>Current reported size of this storage bin, in bytes.</p><p>Does not include data outside of your control that we add during the serialization process, such as encryption stuff, headers, section data, etc. </p> | | ||
| `IsEncrypted` | `bool` | Whether this storage bin is encrypted on disk. Always `false` for Public bins. | | ||
| `Path` | `string` | Path of the file on disk. | | ||
|
||
#### Methods | ||
|
||
| Name | Notes | | ||
| --- | --- | | ||
| `GetBoolean(string key) : bool?` | Get the value of a key as a `boolean`, or `nil` if the key is `nil` or not present. | | ||
| `GetNumber(string key) : number?` | Get the value of a key as a `number`, or `nil` if the key is `nil` or missing. | | ||
| `GetString(string key) : string?` | Get the value of a key as a `string`, or `nil` if the key is `nil` or missing. | | ||
| `GetTable(string key) : table?` | Get the value of a key as a `table`, or `nil` if the key is `nil` or missing. | | ||
| `HasValue(string key) : bool` | Determine if the given named `key` is present in the underlying `table`. | | ||
| `Load() : void` | Loads data from disk, if present. | | ||
| `Save() : void` | <p>If anything has changed (if the storage is marked dirty), save to disk.</p><p>A successful save will clear dirtiness.</p> | | ||
| `SetBoolean(string key, bool? value) : void` | <p>Set the value of a key to a `boolean` or `nil`.</p><p>You will need to call `Save()` to commit this change to disk.</p> | | ||
| `SetNumber(string key, number? value) : void` | <p>Set the value of a key to a `number` or `nil`.</p><p>You will need to call `Save()` to commit this change to disk.</p> | | ||
| `SetString(string key, string? value) : void` | <p>Set the value of a key to a `string` or `nil`.</p><p>You will need to call `Save()` to commit this change to disk.</p> | | ||
| `SetTable(string key, table? value) : void` | <p>Set the value of a key to a `table` or `nil`.</p><p>**IMPORTANT:** Any values in the provided table or subtables that are not `nil`, `number`, `boolean`, `table`, or `string` will be silently stripped during serialization!</p><p>You will need to call `Save()` to commit this change to disk.</p> | | ||
|
||
### Private | ||
|
||
The Private bin is accessible as `Storage.Private`. This bin's corresponding file on disk is encrypted and stored as a binary format, as it is intended to be used for storing deliberately opaque data, such as a user's score in a game, character levels, *et cetera*. | ||
|
||
#### Properties | ||
|
||
| Name | Type | Notes | | ||
| --- | --- | --- | | ||
| `BytesAllowed` | `int` | <p>How many bytes are allowed for this storage bin, in total. Currently set to `4194304`, which corresponds to 4MiB.</p><p>Does not include data outside of your control that we add during the serialization process, such as encryption stuff, headers, section data, etc.</p> | | ||
| `CurrentSize` | `int` | <p>Current reported size of this storage bin, in bytes.</p><p>Does not include data outside of your control that we add during the serialization process, such as encryption stuff, headers, section data, etc.</p> | | ||
| `IsEncrypted` | `bool` | Whether this storage bin is encrypted on disk. Always `false` for Private bins. | | ||
| `Path` | `string` | Path of the file on disk. | | ||
|
||
#### Methods | ||
|
||
| Name | Notes | | ||
| --- | --- | | ||
| `GetBoolean(string key) : bool?` | Get the value of a key as a `boolean`, or `nil` if the key is `nil` or not present. | | ||
| `GetNumber(string key) : number?` | Get the value of a key as a `number`, or `nil` if the key is `nil` or missing. | | ||
| `GetString(string key) : string?` | Get the value of a key as a `string`, or `nil` if the key is `nil` or missing. | | ||
| `GetTable(string key) : table?` | Get the value of a key as a `table`, or `nil` if the key is `nil` or missing. | | ||
| `HasValue(string key) : bool` | Determine if the given named `key` is present in the underlying `table`. | | ||
| `Load() : void` | Loads data from disk, if present. | | ||
| `Save() : void` | <p>If anything has changed (if the storage is marked dirty), save to disk.</p><p>A successful save will clear dirtiness.</p> | | ||
| `SetBoolean(string key, bool? value) : void` | <p>Set the value of a key to a `boolean` or `nil`.</p><p>You will need to call `Save()` to commit this change to disk.</p> | | ||
| `SetNumber(string key, number? value) : void` | <p>Set the value of a key to a `number` or `nil`.</p><p>You will need to call `Save()` to commit this change to disk.</p> | | ||
| `SetString(string key, string? value) : void` | <p>Set the value of a key to a `string` or `nil`.</p><p>You will need to call `Save()` to commit this change to disk.</p> | | ||
| `SetTable(string key, table? value) : void` | <p>Set the value of a key to a `table` or `nil`.</p><p>**IMPORTANT:** Any values in the provided table or subtables that are not `nil`, `number`, `boolean`, `table`, or `string` will be silently stripped during serialization!</p><p>You will need to call `Save()` to commit this change to disk.</p> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# Persistence | ||
|
||
The following script is a short demonstration of a common use case for storing persistence data: A hypothetical RPG game! (We've stripped out the actual RPG code to make things simpler to understand.) | ||
|
||
For more information on the Storage API, please read our [Storage API Reference](../api/storage.md). | ||
|
||
**This is stored on the user's computer, so it's not entirely secure!** | ||
|
||
```lua | ||
-- This is a world script, but you can use persistence on worlds, avatars, and props! | ||
|
||
-- A table of the players in our game. In our case, we'll use a {user guid => data} structure. | ||
PLAYERS = {} | ||
|
||
-- Version of our game's public storage scheme, explanation below | ||
LATEST_PUBLIC_VERSION = 5 | ||
|
||
-- Same for private storage | ||
LATEST_PRIVATE_VERSION = 13 | ||
|
||
-- Game difficulty (you'd use constants here, like DIFFICULTY_EASY=1, _MEDIUM=2, _HARD=3; but let's be short and sweet for this example) | ||
DIFFICULTY=2 | ||
|
||
-- Turn off Krampus mobs by defaults because they scared the American playtesters | ||
KRAMPUS_ENABLED=false | ||
function Start() | ||
print("HYPOTHETICAL-RPG V1.0") | ||
|
||
-- Print out some helpful debug info | ||
print("[Public Storage]") | ||
print("Path..........: " .. Storage.Public.Path) | ||
print("Bytes Allowed.: " .. tostring(Storage.Public.BytesAllowed)) | ||
print("Bytes In Use..: " .. tostring(Storage.Public.CurrentSize)) | ||
print("[Private Storage]") | ||
print("Path..........: " .. Storage.Private.Path) | ||
print("Bytes Allowed.: " .. tostring(Storage.Private.BytesAllowed)) | ||
print("Bytes In Use..: " .. tostring(Storage.Private.CurrentSize)) | ||
|
||
-- First, we'll load up our public configuration data. | ||
local public = Storage.Public | ||
--[[ WALL OF TEXT WARNING | ||
At this point, if there's an existing file, it's already Load()'d, so we don't need to call it here. It should already be loaded into memory if it's there on disk. If it's NOT present on disk, we're just basically working with a blank file with no data. We can add this later. | ||
So, a good way to check for file presence while also tracking changes to the data structure we make over time (like adding new stats or renaming our 'spel' list to 'spell'), we try to access a field called version. | ||
Version is a number because we can just increment it one to say we're on a new version. If the save is less than the latest version, we know it's old and can throw it out, or manually upgrade it. | ||
]]-- | ||
local publicVersion = public.GetNumber("version") | ||
|
||
-- If we're starting a new save, it's simply nil, because the file is blank. | ||
if publicVersion == nil then | ||
-- Cool, new save! Let's fill in some defaults for the user. | ||
-- Tell the game this is the latest structure of our save file, since we're making it from scratch. | ||
public.SetNumber("version", LATEST_PUBLIC_VERSION) | ||
-- Set our defaults | ||
public.SetBoolean("krampus_enabled", KRAMPUS_ENABLED) | ||
public.SetNumber("difficulty", DIFFICULTY) | ||
-- And any other stuff you'd like can go here. | ||
-- Now we need to Save() to actually save these changes to disk. | ||
public.Save() | ||
elseif publicVersion < LATEST_PUBLIC_VERSION then | ||
-- We could either reset here as above, or make an if-tree handling and upgrading every prior version of this save. This is outside the scope of this example. | ||
print('ERROR: Public data is out of date! FIXME!') | ||
end | ||
|
||
-- So we have data. Let's load it into our game configuration: | ||
DIFFICULTY = public.GetNumber("difficulty") | ||
KRAMPUS_ENABLED = public.GetBoolean("krampus") | ||
|
||
-- Let's load up our private storage now. | ||
-- This is where we're going to store more sensitive things, like player stats, save state, monster positions, quests, etc. | ||
local private = Storage.Private | ||
-- Check our version info | ||
local privateVersion = private.GetNumber("version") | ||
if privateVersion == nil then | ||
-- New save! Build defaults and save. Don't do class selection or anything here, this is just scaffolding for later. | ||
-- Ideally, this would be player GUID instead of name, and a full-featured "class" with functions, and then you'd just simplify it in SaveGame(), but we're getting ahead of ourselves. | ||
PLAYER["Example"] = { | ||
level=0, | ||
hp=0, | ||
mana=0, | ||
inventory=["sword", "rat flail"], | ||
gold=10 | ||
} | ||
SaveGame() | ||
elseif privateVersion < LATEST_PRIVATE_VERSION then | ||
-- Upgrade saves here | ||
print('ERROR: Private data is out of date! FIXME!') | ||
end | ||
|
||
-- And load it all into memory. | ||
PLAYERS = private.GetTable("players") | ||
|
||
-- Other game startup stuff | ||
end | ||
|
||
-- ... Other game stuff ... | ||
|
||
-- A function to just dump the game state into our save. | ||
function SaveGame() | ||
private.SetNumber("version", LATEST_PRIVATE_VERSION) | ||
-- Remember that if you feed SetTable a table with functions/coroutines/etc, it will silently eat them and you won't get them back when it loads the table later. Allowing Lua to store arbitrary code to a user's computer would be... bad, so we had to make some compromises. | ||
private.SetTable("players", PLAYERS) | ||
end | ||
``` |