### Full Integration Example Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md A comprehensive example demonstrating the full integration of Network Storage within a game's lifecycle, including configuration, UI setup, event handling, and initialization. ```APIDOC ## Full Integration Example ### Description This example shows a complete integration of Network Storage into a game, covering initialization, UI setup, event handling, and configuration. ### Code ```csharp public partial class MyGame : GameManager { private Panel _hud; public override void ClientJoined( IClient cl ) { base.ClientJoined( cl ); if ( cl.IsHost ) { InitializeNetworkStorage(); } } private void InitializeNetworkStorage() { // Configure Network Storage NetworkStorage.Configure( "my-project-id", "sbox_ns_mykey" ); // IMPORTANT: Set the root panel for the popup UI // This must be done before any revision events fire _hud = new MyHudPanel(); // Your game's HUD NetworkStorageOutdatedUI.RootPanel = _hud; // Enable built-in UI NetworkStorage.RevisionSettings.ShowDefaultMessage = true; // Handle player actions from the popup NetworkStorageOutdatedUI.OnCreateNewGame += HandleCreateNewGame; NetworkStorageOutdatedUI.OnContinuePlaying += HandleContinuePlaying; // Initialize revision handler (subscribes to status changes) NetworkStorageRevisionHandler.Initialize(); } private void HandleCreateNewGame() { // Save any unsaved progress first SaveProgress(); // Disconnect and restart Game.Disconnect(); // The player will rejoin on the new revision after game update } private void HandleContinuePlaying() { // Player chose to keep playing on old version // Maybe show a small reminder in the corner ShowPersistentUpdateReminder(); } } ``` ``` -------------------------------- ### Full Network Storage Integration Example Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Integrate Network Storage into your game, including configuration, UI setup, event handling, and revision handler initialization. Ensure the root panel is set before any revision events fire. ```csharp public partial class MyGame : GameManager { private Panel _hud; public override void ClientJoined( IClient cl ) { base.ClientJoined( cl ); if ( cl.IsHost ) { InitializeNetworkStorage(); } } private void InitializeNetworkStorage() { // Configure Network Storage NetworkStorage.Configure( "my-project-id", "sbox_ns_mykey" ); // IMPORTANT: Set the root panel for the popup UI // This must be done before any revision events fire _hud = new MyHudPanel(); // Your game's HUD NetworkStorageOutdatedUI.RootPanel = _hud; // Enable built-in UI NetworkStorage.RevisionSettings.ShowDefaultMessage = true; // Handle player actions from the popup NetworkStorageOutdatedUI.OnCreateNewGame += HandleCreateNewGame; NetworkStorageOutdatedUI.OnContinuePlaying += HandleContinuePlaying; // Initialize revision handler (subscribes to status changes) NetworkStorageRevisionHandler.Initialize(); } private void HandleCreateNewGame() { // Save any unsaved progress first SaveProgress(); // Disconnect and restart Game.Disconnect(); // The player will rejoin on the new revision after game update } private void HandleContinuePlaying() { // Player chose to keep playing on old version // Maybe show a small reminder in the corner ShowPersistentUpdateReminder(); } } ``` -------------------------------- ### Endpoint with Wrapper Layout Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/source-authoring.md This example shows the older wrapper layout for an endpoint, where definition details are nested. The backend can upgrade this to the flat layout. ```yaml sourceVersion: 1 kind: endpoint id: mine-ore definition: method: POST steps: [] ``` -------------------------------- ### Internal Endpoint Example Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/source-authoring.md Example of an internal endpoint definition for debiting currency. ```APIDOC ## Debit Currency (Internal) ### Description Defines an internal endpoint for debiting player currency. ### Method POST ### Endpoint /debit-currency ### Parameters #### Request Body - **amount** (number) - Required - The amount of currency to debit. ### Request Example ```json { "amount": 50 } ``` ### Response #### Success Response (200) - **ok** (boolean) - Indicates if the operation was successful. #### Response Example ```json { "ok": true } ``` ``` -------------------------------- ### MCP Server Configuration for Claude Code Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Configuration for the MCP server, intended for use with Claude Code. This JSON object specifies how to start the MCP server, including installing dependencies and running the server script. ```json { "mcpServers": { "sbox-network-storage": { "command": "bash", "args": ["-c", "cd mcp && bun install —silent && cd .. && bun run mcp/index.ts"] } } } ``` -------------------------------- ### Public Endpoint Example Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/source-authoring.md Example of a public endpoint definition for mining ore. ```APIDOC ## POST /mine-ore ### Description Defines a public endpoint for mining ore, including input validation. ### Method POST ### Endpoint /mine-ore ### Parameters #### Request Body - **amount** (number) - Required - The amount of ore to mine. ### Request Example ```json { "amount": 10 } ``` ### Response #### Success Response (200) - **ok** (boolean) - Indicates if the operation was successful. #### Response Example ```json { "ok": true } ``` ``` -------------------------------- ### NetworkStorage.ApiRoot Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Gets the base URL for the Network Storage API. ```APIDOC ## NetworkStorage.ApiRoot ### Description The full versioned API URL (e.g. `https://api.sboxcool.com/v3`). ### Method `NetworkStorage.ApiRoot` ### Response #### Success Response (200) - **result** (string) - The API root URL. ``` -------------------------------- ### Call Network Storage Endpoints Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Make GET or POST requests to server endpoints. Supports direct slugs or full URLs. Input data is serialized to JSON. Ensure configuration is done before calling. ```csharp // Configure once at startup if ( !NetworkStorage.IsConfigured ) MyNetStorageConfig.Initialize(); // Call a server endpoint (GET) var player = await NetworkStorage.CallEndpoint( "load-player" ); // Call with input (POST) var result = await NetworkStorage.CallEndpoint( "mine-ore", new { ore_id = "iron", kg = 5.0f } ); // Endpoint URLs from the dashboard are also accepted; the slug is extracted. var sameResult = await NetworkStorage.CallEndpoint( "https://api.sboxcool.com/v3/endpoints/your_project_id/mine-ore?apiKey=sbox_ns_your_public_key", new { ore_id = "iron", kg = 5.0f } ); ``` -------------------------------- ### Available Status Properties Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Query `NetworkStoragePackageInfo` to get the current revision state, including outdated status, enforcement mode, and grace period information. ```APIDOC ## Available Status Properties ### Description Query `NetworkStoragePackageInfo` for current revision state. ### Properties | Property | Type | Description | |---|---|---| | `IsOutdatedRevision` | bool | True when running an older revision | | `EnforcementMode` | RevisionEnforcementMode | `ForceUpgrade` or `AllowContinue` | | `GraceRemainingMinutes` | int? | Minutes left in grace period | | `GraceExpired` | bool | True when grace period has ended | | `RevisionAction` | string | Current action: `"warn"`, `"block_writes"`, `"block_all"` | | `RevisionMessage` | string | Server-provided message for players | | `CurrentRevisionId` | long? | The revision this game is running | | `ServerCurrentRevision` | long? | The latest revision on the server | | `PolicyShowUpdateOptions` | bool | Whether server wants update buttons shown | | `PolicyShowPopupOnce` | bool | Whether popup should only show once | ``` -------------------------------- ### Define an Internal Endpoint for Debiting Currency Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/source-authoring.md Use `kind: endpoint` with `exposure: internal` for reusable logic callable only from backend endpoints or workflows. This example debits a specified amount of gold from a player's account. ```yaml sourceVersion: 1 kind: endpoint id: debit-currency name: Debit Currency exposure: internal params: amount: type: number steps: - id: debit type: write collection: players key: "{{steamId}}" ops: - op: inc path: gold value: "{{-params.amount}}" ``` -------------------------------- ### Define a Public Endpoint for Mining Ore Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/source-authoring.md Use `kind: endpoint` with `exposure: public` to create a game API endpoint. This example defines an input for the amount of ore to mine and includes validation to ensure the amount is positive. ```yaml sourceVersion: 1 kind: endpoint id: mine-ore name: Mine Ore exposure: public method: POST input: type: object properties: amount: type: number steps: - id: validate_amount type: condition check: field: "{{input.amount}}" op: ">" value: 0 routes: false: action: reject status: 400 error: INVALID_AMOUNT message: Amount must be positive. webhook: true response: status: 200 body: ok: true ``` -------------------------------- ### Call Server-Side Endpoint Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Invoke server-side endpoints using their slug or full dashboard URL. Use GET for no input, POST for input objects. Returns `JsonElement?`, with null indicating an error. Supports proxy routing for non-host clients. ```csharp // GET endpoint (no input) var playerData = await NetworkStorage.CallEndpoint( "load-player" ); if ( playerData.HasValue ) { var name = JsonHelpers.GetString( playerData.Value, "playerName", "Unknown" ); var level = JsonHelpers.GetInt( playerData.Value, "level", 1 ); var currency = JsonHelpers.GetInt( playerData.Value, "currency", 0 ); Log.Info( $"Loaded: {name} Lv.{level} | {currency} coins" ); } // POST endpoint (with input) var mineResult = await NetworkStorage.CallEndpoint( "mine-ore", new { ore_id = "iron", kg = 5.0f } ); if ( mineResult.HasValue ) Log.Info( "Mining succeeded" ); else Log.Warning( "Mining failed — check server logs" ); // Full dashboard URL also accepted; the slug is extracted automatically var sameResult = await NetworkStorage.CallEndpoint( "https://api.sboxcool.com/v3/endpoints/your_project_id/mine-ore?apiKey=sbox_ns_your_key", new { ore_id = "iron", kg = 5.0f } ); ``` -------------------------------- ### Initialize Network Storage v3 Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Configure Network Storage with your ProjectId and ApiKey. Call this once at game startup. ```csharp public static class NetworkStorageConfig { public const string ProjectId = "YOUR_PROJECT_ID"; public const string ApiKey = "sbox_ns_YOUR_KEY"; public static void Initialize() { NetworkStorage.Configure( ProjectId, ApiKey ); } } ``` -------------------------------- ### Configure Network Storage Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Define project ID and API key, then initialize the Network Storage client. Call Initialize once at game startup. ```csharp namespace Sandbox; public static class MyNetStorageConfig { public const string ProjectId = "your_project_id"; public const string ApiKey = "sbox_ns_your_public_key"; public static void Initialize() { NetworkStorage.Configure( ProjectId, ApiKey ); } } ``` -------------------------------- ### NetworkStorage.Configure Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Initializes the Network Storage runtime client with project credentials. This must be called before any other Network Storage operations. ```APIDOC ## NetworkStorage.Configure ### Description One-time initialization of the runtime client with project credentials. Must be called before any endpoint, document, or game-value calls. The revision handler is initialized automatically on configure. ### Method Signature `NetworkStorage.Configure(string projectId, string apiKey, string baseUrl = null, string apiVersion = null)` ### Parameters - **projectId** (string) - Required - Your project ID from the sboxcool.com dashboard. - **apiKey** (string) - Required - Your public API key (starts with `sbox_ns_`). - **baseUrl** (string) - Optional - The base URL for the API. Defaults to the sboxcool.com API. - **apiVersion** (string) - Optional - The API version to use. Defaults to `v3`. ### Usage Example ```csharp public static class MyNetStorageConfig { public const string ProjectId = "your_project_id"; // from sboxcool.com dashboard public const string ApiKey = "sbox_ns_your_key"; // public key — safe in game code public static void Initialize() { NetworkStorage.Configure( ProjectId, ApiKey ); // Optional: override base URL or API version for self-hosted backends // NetworkStorage.Configure( ProjectId, ApiKey, baseUrl: "https://api.sboxcool.com", apiVersion: "v3" ); Log.Info( $"[MyGame] NetworkStorage ready — {NetworkStorage.ApiRoot}" ); } } // In your Component.OnStart or GameManager: protected override void OnStart() { if ( IsProxy ) return; if ( !NetworkStorage.IsConfigured ) MyNetStorageConfig.Initialize(); } ``` ``` -------------------------------- ### Initialize Network Storage Client Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Call this once during game initialization to configure the Network Storage runtime client with your project credentials. Ensure this is called before any other Network Storage operations. ```csharp // In your game's initialization code (e.g., GameManager.OnStart or a static config class) public static class MyNetStorageConfig { public const string ProjectId = "your_project_id"; // from sboxcool.com dashboard public const string ApiKey = "sbox_ns_your_key"; // public key — safe in game code public static void Initialize() { NetworkStorage.Configure( ProjectId, ApiKey ); // Optional: override base URL or API version for self-hosted backends // NetworkStorage.Configure( ProjectId, ApiKey, baseUrl: "https://api.sboxcool.com", apiVersion: "v3" ); Log.Info( $"[MyGame] NetworkStorage ready — {NetworkStorage.ApiRoot}" ); } } // In your Component.OnStart or GameManager: protected override void OnStart() { if ( IsProxy ) return; if ( !NetworkStorage.IsConfigured ) MyNetStorageConfig.Initialize(); } ``` -------------------------------- ### NetworkStorage.Configure Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Configures the Network Storage client with project ID and API key. This should be called once at startup. ```APIDOC ## NetworkStorage.Configure ### Description Set credentials for Network Storage. Call once at startup. ### Method `NetworkStorage.Configure(projectId, apiKey)` ### Parameters #### Path Parameters - **projectId** (string) - Required - The project identifier. - **apiKey** (string) - Required - The public API key. ### Request Example ```csharp NetworkStorage.Configure( "your_project_id", "sbox_ns_your_public_key" ); ``` ``` -------------------------------- ### NetworkStorage.CallEndpoint Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Calls a server endpoint using its slug or full URL. Supports GET and POST requests with optional input data. ```APIDOC ## NetworkStorage.CallEndpoint ### Description Call a server endpoint by slug or endpoint URL. Returns `JsonElement?`. ### Method `NetworkStorage.CallEndpoint(slugOrUrl, input?) ### Parameters #### Path Parameters - **slugOrUrl** (string) - Required - The endpoint slug or full URL. - **input** (object, optional) - The input data for the endpoint (used for POST requests). ### Request Example ```csharp // GET request var player = await NetworkStorage.CallEndpoint( "load-player" ); // POST request var result = await NetworkStorage.CallEndpoint( "mine-ore", new { ore_id = "iron", kg = 5.0f } ); // Using a full URL var sameResult = await NetworkStorage.CallEndpoint( "https://api.sboxcool.com/v3/endpoints/your_project_id/mine-ore?apiKey=sbox_ns_your_public_key", new { ore_id = "iron", kg = 5.0f } ); ``` ### Response #### Success Response (200) - **result** (JsonElement?) - The JSON response from the endpoint, or null if an error occurred. ``` -------------------------------- ### Launch Dedicated Server with Network Storage Secret Key Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Launch a dedicated server using `sbox-server.exe` and provide the Network Storage secret key via a launch argument. This key is validated at startup and used for all subsequent requests. ```bash # Launch a dedicated server with the Network Storage secret key sbox-server.exe \ +game your.org.game \ your.map \ +hostname "My Server" \ +network_storage_secret_key sbox_sk_your_secret_key # Accepted key aliases (all equivalent): # +network-storage-secret-key # +sboxcool_secret_key # +networkStorageSecretKey # +nsSecretKey # +ns_secret_key ``` -------------------------------- ### NetworkStorage.CallEndpoint Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Calls a server-side endpoint by its slug or full dashboard URL. Supports GET requests with no input and POST requests with input data. Returns `JsonElement?`, with null indicating an error. ```APIDOC ## NetworkStorage.CallEndpoint ### Description Call a server-side endpoint by slug (or full dashboard URL). Passing no `input` sends a GET; passing an `input` object sends a POST. Returns `JsonElement?` — null on any error. Supports proxy routing through the game host for non-host clients when `ProxyEnabled = true`. ### Method Signature `Task CallEndpoint(string slugOrUrl, object input = null)` ### Parameters - **slugOrUrl** (string) - Required - The slug of the endpoint or its full dashboard URL. - **input** (object) - Optional - The input data for the endpoint. If provided, the request will be a POST; otherwise, it will be a GET. ### Request Example (GET) ```csharp var playerData = await NetworkStorage.CallEndpoint( "load-player" ); if ( playerData.HasValue ) { var name = JsonHelpers.GetString( playerData.Value, "playerName", "Unknown" ); var level = JsonHelpers.GetInt( playerData.Value, "level", 1 ); var currency = JsonHelpers.GetInt( playerData.Value, "currency", 0 ); Log.Info( $"Loaded: {name} Lv.{level} | {currency} coins" ); } ``` ### Request Example (POST) ```csharp var mineResult = await NetworkStorage.CallEndpoint( "mine-ore", new { ore_id = "iron", kg = 5.0f } ); if ( mineResult.HasValue ) Log.Info( "Mining succeeded" ); else Log.Warning( "Mining failed — check server logs" ); ``` ### Request Example (Full URL) ```csharp var sameResult = await NetworkStorage.CallEndpoint( "https://api.sboxcool.com/v3/endpoints/your_project_id/mine-ore?apiKey=sbox_ns_your_key", new { ore_id = "iron", kg = 5.0f } ); ``` ### Response - **JsonElement?** - Returns a `JsonElement?`. A null value indicates any failure during the call. ``` -------------------------------- ### Load Game Values from Network Storage Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Fetches all game values (constants and tables) for the configured project. Call this once at startup and cache the result. No authentication is required. ```csharp public static class GameConfig { public record OreInfo( string Id, string Name, int Tier, float ValuePerKg ); public static bool IsLoaded { get; private set; } public static Dictionary OreTypes { get; private set; } = new(); public static int StartingCurrency { get; private set; } = 500; public static int XpPerLevel { get; private set; } = 100; public static async Task Load() { var response = await NetworkStorage.GetGameValues(); if ( !response.HasValue ) { Log.Warning( "Failed to load game values — using defaults" ); return; } var data = response.Value; // Constants are organized as nested groups if ( data.TryGetProperty( "constants", out var constants ) ) { if ( constants.TryGetProperty( "progression", out var prog ) ) { XpPerLevel = JsonHelpers.GetInt( prog, "xp_per_level", 100 ); StartingCurrency = JsonHelpers.GetInt( prog, "starting_currency", 500 ); } } // Tables are arrays of row objects if ( data.TryGetProperty( "tables", out var tables ) ) { if ( tables.TryGetProperty( "ore_types", out var oreTable ) ) { var rows = oreTable.ReadList( "rows", row => new OreInfo( JsonHelpers.GetString( row, "id", "" ), JsonHelpers.GetString( row, "name", "" ), JsonHelpers.GetInt( row, "tier", 1 ), JsonHelpers.GetFloat( row, "value_per_kg", 1f ) ) ); foreach ( var ore in rows ) OreTypes[ore.Id] = ore; } } IsLoaded = true; Log.Info( $"[GameConfig] Loaded {OreTypes.Count} ore types, xp/level={XpPerLevel}" ); } } ``` -------------------------------- ### Test Network Storage UI and Events in Editor Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Simulate the revision outdated UI and events for testing purposes without needing to publish a new version. This bypasses the ShowOnlyOnce logic. ```csharp // Show the full UI + fire events (bypasses ShowOnlyOnce) NetworkStorage.TestShowRevisionOutdatedMessage( parentPanel ); // Fire the event only (test custom hooks without UI) NetworkStorage.TestFireRevisionOutdated(); ``` -------------------------------- ### Clone sbox-network-storage Repository Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Use this command to clone the repository into your project's Libraries directory. ```bash cd "YourProject/Libraries" git clone https://github.com/sbox-cool/sbox-network-storage "Network Storage by sboxcool" ``` -------------------------------- ### Enable Built-in Popup UI for Network Storage Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Configure the built-in popup UI for network storage revision warnings. Set the root panel and control popup behavior like showing only once, auto-opening, and allowing space to close. ```csharp // ═══════════════════════════════════════════════════════════════════ // OPTION 1: Enable the built-in popup UI // ═══════════════════════════════════════════════════════════════════ // IMPORTANT: Set the root panel so the popup knows where to attach // Do this once when your HUD is created (e.g., in your GameManager or Hud class) NetworkStorageOutdatedUI.RootPanel = myHudPanel; // or Game.RootPanel, your Hud, etc. // Enable the default revision warning popup NetworkStorage.RevisionSettings.ShowDefaultMessage = true; // Show popup only once per session (default: true) NetworkStorage.RevisionSettings.ShowOnlyOnce = true; // Auto-open popup when outdated is detected (default: true) NetworkStorage.RevisionSettings.AutoOpenOnOutdated = true; // Allow SPACE key to close the popup (default: true) NetworkStorage.RevisionSettings.AllowSpaceToClose = true; ``` -------------------------------- ### Add sbox-network-storage as Git Submodule Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Alternatively, add the repository as a git submodule to your project. ```bash git submodule add https://github.com/s-box-cool/sbox-network-storage "Libraries/Network Storage by sboxcool" ``` -------------------------------- ### Build and Check Lobby Metadata Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Use BuildLobbyMetadata to stamp lobbies with revision info at creation. IsLobbyOnCurrentRevision and IsLobbyStale check metadata without network calls. ```csharp // When creating a lobby, stamp it with current revision info var meta = NetworkStorage.BuildLobbyMetadata( migrationRevision: null, // set when launching a "Create New Game" migration sourceLobbyId: null // previous lobby ID during "Create New Game" flow ); lobby.SetMetadata( meta ); // When the player creates a new session after an update var migrationMeta = NetworkStorage.BuildLobbyMetadata( migrationRevision: NetworkStoragePackageInfo.ServerCurrentRevision, sourceLobbyId: currentLobbyId ); // Filter lobby list to current-version lobbies var freshLobbies = allLobbies .Where( l => NetworkStorage.IsLobbyOnCurrentRevision( l.GetMetadata() ) ) .ToList(); // Check if a specific lobby is stale bool stale = NetworkStorage.IsLobbyStale( someLobby.GetMetadata() ); ``` -------------------------------- ### Control Network Storage UI Programmatically Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Open, close, or check the visibility of the built-in Network Storage popup. Use ResetState to allow the popup to show again after being hidden by ShowPopupOnce. ```csharp // Open the built-in popup manually (requires parent panel) NetworkStorageOutdatedUI.Open( myHudPanel ); // Close the popup NetworkStorageOutdatedUI.Close(); // Check if popup is currently visible if ( NetworkStorageOutdatedUI.IsOpen ) { // ... } // Reset popup state - allows popup to show again even with ShowPopupOnce // Useful when starting a new game session or after player reconnects NetworkStorageOutdatedUI.ResetState(); ``` -------------------------------- ### NetworkStorage Revision Settings Configuration Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Configure how the client behaves when running an outdated game revision. Options include using the built-in UI, implementing a custom event handler, or directly polling the revision status. ```csharp // Option 1: Built-in popup UI NetworkStorageOutdatedUI.RootPanel = myHudPanel; // required for auto-attach NetworkStorage.RevisionSettings.ShowDefaultMessage = true; NetworkStorage.RevisionSettings.ShowOnlyOnce = true; NetworkStorage.RevisionSettings.AutoOpenOnOutdated = true; NetworkStorage.RevisionSettings.AllowSpaceToClose = true; // Option 2: Custom UI via event NetworkStorage.RevisionSettings.ShowDefaultMessage = false; NetworkStorage.OnRevisionOutdated += data => { var mode = NetworkStoragePackageInfo.EnforcementMode; if ( mode == RevisionEnforcementMode.AllowContinue ) MyUI.ShowInfoBanner( "A new version is available!" ); else MyUI.ShowWarning( $"Update required in {data.GraceSeconds / 60} min" ); }; // Option 3: Poll status directly if ( NetworkStoragePackageInfo.IsOutdatedRevision ) { var action = NetworkStoragePackageInfo.RevisionAction; // "warn", "block_writes", "block_all" var expired = NetworkStoragePackageInfo.GraceExpired; var msg = NetworkStoragePackageInfo.RevisionMessage; Log.Warning( $"Outdated: action={action} expired={expired} msg={msg}" ); } // Disable revision tracking entirely NetworkStorage.RevisionSettings.Enabled = false; ``` -------------------------------- ### Build and Check Lobby Metadata for Revision Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Stamp lobby metadata with revision tracking information when creating a lobby. Check if a lobby is using the current revision or is stale. ```csharp var meta = NetworkStorage.BuildLobbyMetadata( migrationRevision: null, sourceLobbyId: null ); lobby.SetMetadata( meta ); // Check lobby freshness bool isFresh = NetworkStorage.IsLobbyOnCurrentRevision( metadata ); bool isStale = NetworkStorage.IsLobbyStale( metadata ); ``` -------------------------------- ### NetworkStorage.RevisionSettings Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Configuration settings for revision detection in Network Storage. ```APIDOC ## NetworkStorage.RevisionSettings ### Description Configuration settings for revision detection in Network Storage. ### Settings | Setting | Type | Default | Description | |---|---|---|---| | `Enabled` | bool | `true` | Enable/disable all revision detection | | `ShowDefaultMessage` | bool | `false` | Show built-in popup UI | | `ShowOnlyOnce` | bool | `true` | Show popup once per session | | `AutoOpenOnOutdated` | bool | `true` | Auto-open popup when outdated detected | | `AllowSpaceToClose` | bool | `true` | SPACE key closes popup | | `GracePeriod` | TimeSpan | Zero | Client-side grace before showing UI | | `AutoRefreshLobbies` | bool | `false` | Auto-refresh lobby list when outdated | | `LobbyRefreshInterval` | float | `10` | Seconds between lobby refreshes | ``` -------------------------------- ### Use Custom UI with Network Storage Events Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Implement a custom UI for network storage revision warnings by disabling the default message and subscribing to the OnRevisionOutdated event. Handle different enforcement modes and display appropriate notifications. ```csharp // ═══════════════════════════════════════════════════════════════════ // OPTION 2: Use your own custom UI (recommended for polished games) // ═══════════════════════════════════════════════════════════════════ // Keep the default UI disabled NetworkStorage.RevisionSettings.ShowDefaultMessage = false; // Subscribe to the event and show your own UI NetworkStorage.OnRevisionOutdated += ( data ) => { Log.Info( $"Outdated: rev {data.CurrentRevisionId} -> {data.LatestRevisionId}" ); // Check enforcement mode to decide UI style var mode = NetworkStoragePackageInfo.EnforcementMode; if ( mode == RevisionEnforcementMode.AllowContinue ) { // Show a gentle notification - player can dismiss and keep playing MyUI.ShowInfoBanner( "A new version is available!" ); } else { // Show a warning with countdown var remaining = NetworkStoragePackageInfo.GraceRemainingMinutes; MyUI.ShowWarning( $"Update required in {remaining} minutes" ); } }; ``` -------------------------------- ### NetworkStorage.BuildLobbyMetadata and Revision Checks Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Methods for creating lobby metadata with revision information and checking if a lobby is on the current revision or stale without network calls. ```APIDOC ## NetworkStorage.BuildLobbyMetadata / IsLobbyOnCurrentRevision Stamp lobbies with revision metadata at creation time so browsers can filter to current-version lobbies. `IsLobbyOnCurrentRevision` and `IsLobbyStale` check lobby metadata without a network call. ```csharp // When creating a lobby, stamp it with current revision info var meta = NetworkStorage.BuildLobbyMetadata( migrationRevision: null, // set when launching a "Create New Game" migration sourceLobbyId: null // previous lobby ID during "Create New Game" flow ); lobby.SetMetadata( meta ); // When the player creates a new session after an update var migrationMeta = NetworkStorage.BuildLobbyMetadata( migrationRevision: NetworkStoragePackageInfo.ServerCurrentRevision, sourceLobbyId: currentLobbyId ); // Filter lobby list to current-version lobbies var freshLobbies = allLobbies .Where( l => NetworkStorage.IsLobbyOnCurrentRevision( l.GetMetadata() ) ) .ToList(); // Check if a specific lobby is stale bool stale = NetworkStorage.IsLobbyStale( someLobby.GetMetadata() ); ``` ``` -------------------------------- ### Handle Built-in UI Events for Network Storage Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/docs.md Subscribe to events triggered by the built-in network storage outdated UI to handle player actions. These events allow you to manage player choices like continuing to play, creating a new session, or joining a new lobby. ```csharp // Player clicked "Continue Playing" (Allow Continue mode only) NetworkStorageOutdatedUI.OnContinuePlaying += () => { Log.Info( "Player chose to continue on old version" ); }; // Player clicked "Create New Session" NetworkStorageOutdatedUI.OnCreateNewGame += () => { // Disconnect from current game and create a fresh session Game.Disconnect(); CreateNewLobby(); }; // Player clicked "Join New Lobby" NetworkStorageOutdatedUI.OnJoinNewLobby += () => { // Show lobby browser filtered to current-revision lobbies ShowLobbyBrowser( filterToCurrentRevision: true ); }; // Player dismissed the popup (Force Upgrade mode only) NetworkStorageOutdatedUI.OnDismiss += () => { Log.Info( "Player dismissed revision warning" ); }; ``` -------------------------------- ### Use NetworkStorageExtensions for JsonElement Shorthands Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Shorthand extension methods on JsonElement that wrap JsonHelpers, plus helpers for parsing arrays and dictionaries from API responses. ```csharp var data = result.Value; // Shorthand scalar access string name = data.Str( "playerName", "Unknown" ); int level = data.Int( "level", 1 ); float speed = data.Float( "speed", 1.0f ); bool flag = data.Bool( "active", false ); // Parse a string array List upgrades = data.ReadStringList( "purchasedUpgrades" ); // => ["speed_boost", "armor_plate"] // Parse an object into a dictionary with a value converter Dictionary inventory = data.ReadDictionary( "ores", v => (float)v.GetDouble() ); // => { "iron": 12.5, "gold": 3.0 } // Parse a table (array of row objects) into typed records var items = data.ReadList( "rows", row => new ItemInfo( row.Str( "id" ), row.Str( "name" ), row.Int( "tier" ) ) ); // Diff local vs server state for diagnostics var mismatches = data.FindMismatches( new Dictionary)> { ["currency"] = (localCurrency, el => (object)JsonHelpers.GetInt( el, "currency", 0 )), ["level"] = (localLevel, el => (object)JsonHelpers.GetInt( el, "level", 1 )), } ); if ( mismatches.Count > 0 ) Log.Warning( $"State drift: {string.Join( ", ", mismatches )}" ); ``` -------------------------------- ### NetworkStorage.GetGameValues Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Fetches all game values, including constants and tables, for the configured project. This method does not require authentication and should be called once at startup and cached. ```APIDOC ## NetworkStorage.GetGameValues ### Description Fetch all game values (constants and tables) for the configured project. Returns the full merged JSON payload. No authentication required — game values are public. Call once at startup and cache the result. ### Method GET (implied) ### Endpoint /game-values ### Response #### Success Response (200) - **data** (JSON) - The merged JSON payload containing constants and tables. ``` -------------------------------- ### Local Source File Validation and Sync Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Commands to validate local source files using `source_compiler.py` and perform a dry run of the sync process using `sync.py`. These scripts help ensure local files are correctly formatted and compatible with the remote server. ```powershell python Libraries/sboxcool.network-storage/Editor/source_compiler.py --project-root . python Libraries/sboxcool.network-storage/Editor/sync.py --project-root . --sources --dry-run ``` -------------------------------- ### Configure Dedicated Server Endpoint Secret Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Launch dedicated servers with a runtime endpoint secret using this command. The library accepts several aliases for the secret key launch parameter. ```bash sbox-server.exe +game your.org.game your.map +hostname "My Server" +network_storage_secret_key sbox_sk_your_secret_key ``` -------------------------------- ### NetworkStorageExtensions (JsonElement extension methods) Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Shorthand extension methods on `JsonElement` that wrap `JsonHelpers`, plus helpers for parsing arrays and dictionaries from API responses. ```APIDOC ## NetworkStorageExtensions (JsonElement extension methods) ### Description Shorthand extension methods on `JsonElement` that wrap `JsonHelpers`, plus helpers for parsing arrays and dictionaries from API responses. ### Methods #### Str Shorthand for `JsonHelpers.GetString`. ```csharp string Str( this JsonElement element, string key, string fallback ); ``` #### Int Shorthand for `JsonHelpers.GetInt`. ```csharp int Int( this JsonElement element, string key, int fallback ); ``` #### Float Shorthand for `JsonHelpers.GetFloat`. ```csharp float Float( this JsonElement element, string key, float fallback ); ``` #### Bool Shorthand for `JsonHelpers.GetBool`. ```csharp bool Bool( this JsonElement element, string key, bool fallback ); ``` #### ReadStringList Parses a string array from a `JsonElement`. ```csharp List ReadStringList( this JsonElement element, string key ); ``` #### ReadDictionary Parses an object into a dictionary with a value converter. ```csharp Dictionary ReadDictionary( this JsonElement element, string key, Func converter ); ``` #### ReadList Parses a table (array of row objects) into typed records. ```csharp List ReadList( this JsonElement element, string key, Func converter ); ``` #### FindMismatches Compares local data with server state for diagnostics. ```csharp Dictionary FindMismatches( this JsonElement element, Dictionary serverValueConverter)> comparisonData ); ``` ### Request Example ```csharp var data = result.Value; // Shorthand scalar access string name = data.Str( "playerName", "Unknown" ); int level = data.Int( "level", 1 ); float speed = data.Float( "speed", 1.0f ); bool flag = data.Bool( "active", false ); // Parse a string array List upgrades = data.ReadStringList( "purchasedUpgrades" ); // => ["speed_boost", "armor_plate"] // Parse an object into a dictionary with a value converter Dictionary inventory = data.ReadDictionary( "ores", v => (float)v.GetDouble() ); // => { "iron": 12.5, "gold": 3.0 } // Parse a table (array of row objects) into typed records var items = data.ReadList( "rows", row => new ItemInfo( row.Str( "id" ), row.Str( "name" ), row.Int( "tier" ) ) ); // Diff local vs server state for diagnostics var mismatches = data.FindMismatches( new Dictionary)> { ["currency"] = (localCurrency, el => (object)JsonHelpers.GetInt( el, "currency", 0 )), ["level"] = (localLevel, el => (object)JsonHelpers.GetInt( el, "level", 1 )) } ); if ( mismatches.Count > 0 ) Log.Warning( $"State drift: {string.Join( ", ", mismatches )}" ); ``` ``` -------------------------------- ### Validate and Sync Source Files Locally Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Utilize the `source_compiler.py` and `sync.py` scripts for local validation and synchronization of YAML source files. The `--dry-run` flag allows previewing changes without uploading. ```powershell # Validate all source files in the project python Libraries/sboxcool.network-storage/Editor/source_compiler.py --project-root . ``` ```powershell # Validate a single file and print JSON output python Libraries/sboxcool.network-storage/Editor/source_compiler.py \ --file "Editor/Network Storage/endpoints/mine-ore.endpoint.yml" --json ``` ```powershell # Dry-run sync (validate + upgrade, no upload) python Libraries/sboxcool.network-storage/Editor/sync.py --project-root . --sources --dry-run ``` ```powershell # Full sync: validate → upgrade → upload sources python Libraries/sboxcool.network-storage/Editor/sync.py --project-root . --sources ``` -------------------------------- ### JsonElement Extension Methods Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Shorthand wrappers around JsonHelpers and collection parsers for `JsonElement` objects. ```APIDOC ## JsonElement Extension Methods ### Description Shorthand wrappers around JsonHelpers, plus collection parsers. ### Methods - `Str(key, defaultValue)`: Shorthand for `GetString`. - `Int(key, defaultValue)`: Shorthand for `GetInt`. - `Float(key, defaultValue)`: Shorthand for `GetFloat`. - `ReadStringList(key)`: Parses a JSON array of strings. - `ReadDictionary(key, valueParser)`: Parses a JSON object into a dictionary. - `ReadList(key, rowParser)`: Parses a JSON array of objects into a list. ### Request Example ```csharp // Shorthand var name = data.Str( "playerName", "Unknown" ); var level = data.Int( "level", 1 ); var speed = data.Float( "speed", 1.0f ); // Parse arrays var upgrades = data.ReadStringList( "purchasedUpgrades" ); // Parse objects var ores = data.ReadDictionary( "ores", v => (float)v.GetDouble() ); // Parse table rows var items = data.ReadList( "rows", row => new ItemInfo( row.Str( "id" ), row.Str( "name" ), row.Int( "tier" ) ) ); ``` ``` -------------------------------- ### Check Currency Workflow Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt A reusable workflow defined in YAML to validate if a player has sufficient currency for a given cost. ```APIDOC ## YAML Source Authoring — Workflow Reusable multi-step logic callable from endpoints via `type: call`. Workflows declare typed `params`, optional `budgets` (max iterations, reads, writes, nested calls), and a `returns` block. ```yaml # Editor/Network Storage/workflows/check-currency.workflow.yml sourceVersion: 1 kind: workflow id: check-currency name: Check Currency description: Validate that a player has enough currency for a cost. definition: params: balance: type: number cost: type: number steps: - id: has_currency type: condition check: field: "{{balance}}" op: ">=" value: "{{cost}}" onFail: status: 409 error: INSUFFICIENT_CURRENCY message: Not enough currency. returns: ok: true ``` ``` -------------------------------- ### NetLog for Network Storage Event Logging Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt A static ring buffer for capturing network storage events. Access `Entries` for debugging and use `Info`/`Error` to add custom log messages. Track `Version` for efficient UI updates. ```csharp // Read all captured log entries (auto-populated by NetworkStorage) foreach ( var entry in NetLog.Entries ) { // entry.Time — DateTime // entry.Kind — Request | Response | Error | Info // entry.Tag — endpoint slug, e.g. "mine-ore", "game-values" // entry.Message — human-readable summary Log.Info( $ ``` ```csharp // Add custom entries from game systems NetLog.Info( "crafting", "Player crafted iron_sword" ); NetLog.Error( "shop", "Insufficient currency" ); // Track version for efficient UI refresh (increments on every add/clear) int lastSeenVersion = 0; void OnFrame() { if ( NetLog.Version != lastSeenVersion ) { lastSeenVersion = NetLog.Version; RefreshDebugPanel(); } } // Clear all entries (e.g., on session restart) NetLog.Clear(); ``` -------------------------------- ### Construct NetworkStorageOperation Mutations Source: https://context7.com/sbox-cool/sbox-network-storage/llms.txt Factory class for constructing server-side document mutation operations. Operations are passed to UpdateDocument and applied atomically by the backend. ```csharp // Set — assign a field value var setOp = NetworkStorageOperation.Set( "status", "active", source: "server", reason: "login" ); ``` ```csharp // Increment — add (or subtract) a numeric value; supports audit fields var incOp = NetworkStorageOperation.Increment( "xp", 100, source: "server", reason: "kill-boss" ); ``` ```csharp // Push — append a value to an array field var pushOp = NetworkStorageOperation.Push( "achievements", "first-kill" ); ``` ```csharp // Pull — remove all matching elements from an array var pullOp = NetworkStorageOperation.Pull( "activeBuffs", "poison" ); ``` ```csharp // Remove — remove a field from the document var removeOp = NetworkStorageOperation.Remove( "tempData", null ); ``` ```csharp // Compose multiple operations in one request await NetworkStorage.UpdateDocument( "player-data", Game.SteamId.ToString(), setOp, incOp, pushOp, pullOp ); ``` -------------------------------- ### NetworkStorage.IsConfigured Source: https://github.com/sbox-cool/sbox-network-storage/blob/main/README.md Checks if the Network Storage client has been configured. ```APIDOC ## NetworkStorage.IsConfigured ### Description Returns `true` after `Configure()` has been called, `false` otherwise. ### Method `NetworkStorage.IsConfigured` ### Response #### Success Response (200) - **result** (boolean) - `true` if configured, `false` otherwise. ```