Basic Game Setup

Set up Network Storage in your s&box game from scratch. Install the library, create a project, configure collections, and implement basic data operations.

YAML Source is the Network Storage authoring standard.
Use the s&box Library Manager install for auto-updates. GitHub is best for agents, source review, contributions, and manual installs.
6

Basic Data Operations

Now that everything is set up, let's build a simple gameplay feature: a reward station where players press E to collect gold, with a cooldown to prevent spam.

## What We're Building

A square object in your game world. When a player walks up and holds E, they receive $500 gold. A 30-second cooldown prevents them from collecting again immediately.

## Step 1: Create the Endpoint

You can create endpoints either via the Sync Tool or the dashboard.

**Via Sync Tool:** Create `Editor/Network Storage/endpoints/collect-reward.endpoint.yml`:

```yml
sourceVersion: "1"
kind: endpoint
slug: collect-reward
name: Collect Reward
description: Award gold from a reward station
method: POST
enabled: true
input:
type: object
properties:
station_id:
type: string
required:
- station_id
steps:
- id: award
type: write
collection: players
key: "{{steamId}}_default"
ops:
- op: inc
path: gold
value: 500
source: reward-station
reason: "Collected from station {{input.station_id}}"
response:
status: 200
body:
success: true
gold_awarded: 500
message: "You collected $500!"
```

Then open **Network Storage > Sync Tool** and click **Push** to deploy.

**Via Dashboard:** Go to **Endpoints > New Endpoint**, enter slug `collect-reward`, and paste the configuration above.

## Step 2: Add a Rate Limit

To prevent players from spamming the reward station, add a rate limit rule:

1. Go to your collection settings for `players` on the dashboard
2. Open the **Rate Limits** tab
3. Add a new rule:

| Field | Value |
|-------|-------|
| **Target Field** | `gold` |
| **Source** | `reward-station` |
| **Window** | 30 seconds |
| **Max Changes** | 1 |
| **Action** | Reject |

This means: for the `gold` field, allow only 1 change per 30 seconds from the `reward-station` source. If the player tries again within 30 seconds, the request is rejected.

## Step 3: Game Code

Create the reward station component. The library handles auth, URL building, and response parsing automatically:

```csharp
using Sandbox;

public sealed class RewardStation : Component
{
[Property] public string StationId { get; set; } = "station_1";

private TimeSince lastCollect;
private const float CooldownSeconds = 30f;
private bool isCoolingDown;

protected override void OnUpdate()
{
if ( !IsProxy && Input.Pressed( "use" ) && !isCoolingDown )
{
_ = CollectReward();
}
}

private async Task CollectReward()
{
isCoolingDown = true;

// Library auto-configures from Setup window credentials
// Auth tokens are generated and attached automatically
var result = await NetworkStorage.CallEndpoint( "collect-reward", new
{
station_id = StationId
} );

if ( result.HasValue )
{
var goldAwarded = result.Value.GetProperty( "gold_awarded" ).GetInt32();
Log.Info( $"Collected ${goldAwarded}!" );

// Show UI feedback to the player
}
else
{
// Check s&box console for [NetworkStorage] error details
Log.Warning( "Could not collect reward. Try again later." );
}

// Local cooldown (server also enforces 30s via rate limit)
lastCollect = 0;
await Task.DelaySeconds( CooldownSeconds );
isCoolingDown = false;
}
}
```

No `Configure()` call needed -- the library reads the runtime config created by the Setup window. Your game uses the Project ID + Public Key, while the Secret Key remains editor-only for sync tasks.

## Step 4: Reading Player Data

To display the player's current gold in the UI:

```csharp
// Reads the current player's document from the "players" collection
// Uses Steam ID automatically
var player = await NetworkStorage.GetDocument( "players" );

if ( player.HasValue )
{
int gold = player.Value.GetProperty( "gold" ).GetInt32();
int xp = player.Value.GetProperty( "xp" ).GetInt32();
Log.Info( $"Gold: {gold}, XP: {xp}" );
}
```

## How the Rate Limit Works

```
Player presses E at 0:00 -> Endpoint runs, gold +500 (success)
Player presses E at 0:10 -> Rate limit rejects (too soon)
Player presses E at 0:25 -> Rate limit rejects (still within 30s)
Player presses E at 0:31 -> Endpoint runs, gold +500 (success)
```

The rate limit is enforced server-side. Even if someone bypasses the client-side cooldown, the server rejects the request.

## What's Next

You now have a working game with:
- Credentials configured via the Setup window (no code needed)
- A collection with a schema
- An endpoint that awards gold
- A rate limit to prevent spam
- Game code using the library for auto-configured API calls

From here, you can:
- Add more endpoints for combat, trading, crafting
- Create Game Values for server-side constants
- Add Workflows for complex validation
- Enable the Ledger for audit trails
- Browse the [full documentation](/wiki/network-storage-v3) for advanced features
Tips & Troubleshooting

> **Troubleshooting tips:**
>
> - **"NetworkStorage not configured"**: Open **Network Storage > Setup** and enter your credentials. Click **Save** and **Test Connection**.
> - **"ENDPOINT_NOT_FOUND"**: Check that the endpoint slug matches exactly (`collect-reward`). Make sure you pushed it via the Sync Tool or saved it on the dashboard.
> - **"UNAUTHORIZED"**: The runtime Project ID or public key may be missing or incorrect. Re-open the Setup window and save again.
> - **"RATE_EXCEEDED"**: The rate limit is working! Wait 30 seconds and try again.
> - **"SCHEMA_VALIDATION_FAILED"**: Your write operation does not match the collection schema. Check field names and types.
> - **Gold not updating in UI?**: Make sure you read the document after the endpoint call returns, not before.