The static class defines the client properties for the sboxcool network storage service. It manages API URLs, project credentials, and initialization parameters.
using System;
using System.Threading.Tasks;
namespace Sandbox;
/// <summary>
/// Network Storage client for sboxcool.com.
///
/// Auto-configures from network-storage.credentials.json on first use.
/// You can also call Configure() manually to override.
/// </summary>
public static partial class NetworkStorage
{
/// <summary>Base API URL (no trailing slash).</summary>
public static string BaseUrl { get; private set; } = "https://api.sboxcool.com";
/// <summary>API root segment. Defaults to <c>v3</c>, which the hosted backend and every released server serve. Use <c>api</c> on self-hosted servers 0.5.5 and later (see Configure).</summary>
public static string ApiVersion { get; private set; } = "v3";
/// <summary>Library SemVer, independent of the s&box package and game revisions.</summary>
public const string LibraryVersion = "1.2.1";
/// <summary>Oldest self-hosted server baseline; newer features require capability checks.</summary>
public const string MinServerVersion = "0.4.0";
/// <summary>Library version reported to analytics. Retained as an LTS public API.</summary>
public static string PackageVersion => LibraryVersion;
/// <summary>Your project ID from the sboxcool.com dashboard.</summary>
public static string ProjectId { get; private set; }
/// <summary>Your public API key (sbox_ns_ prefix).</summary>
public static string ApiKey { get; private set; }
/// <summary>True after Configure() or auto-config has loaded valid credentials.</summary>
public static bool IsConfigured => !string.IsNullOrEmpty( ProjectId ) && !string.IsNullOrEmpty( ApiKey );
/// <summary>Optional CDN URL for storage reads.</summary>
public static string CdnUrl { get; private set; }
/// <summary>The full API root, e.g. https://api.sboxcool.com/v3 (or /api on self-hosted servers 0.5.5+ when configured).</summary>
public static string ApiRoot => $"{BaseUrl}/{ApiVersion}";
/// <summary>Whether non-host clients route API calls through the game host.</summary>
public static bool ProxyEnabled { get; set; } = false;
/// <summary>Project flag for fast auth sessions.</summary>
public static bool EnableAuthSessions { get; private set; }
/// <summary>Project flag for encrypted Network Storage requests.</summary>
public static bool EnableEncryptedRequests { get; private set; }
/// <summary>Runtime publish target for Network Storage resources ("live" or staged "next").</summary>
public static string PublishTarget { get; private set; } = "live";
/// <summary>Delegate for proxying endpoint calls through the game host.</summary>
public static Func<string, string, string, string, Task<string>> RequestProxy { get; set; }
/// <summary>Delegate for proxying document reads through the game host.</summary>
public static Func<string, string, string, string, Task<string>> DocumentProxy { get; set; }
/// <summary>True if this client is the network host or single-player.</summary>
public static bool IsHost => !Networking.IsActive || Networking.IsHost;
/// <summary>Configure the client manually. Call once at game startup.</summary>
public static void Configure( string projectId, string apiKey, string baseUrl = null, string apiVersion = null, string cdnUrl = null, string publishTarget = null )
=> Configure( projectId, apiKey, baseUrl, apiVersion, cdnUrl, publishTarget, null );
/// <summary>Configure the client manually and pin the server signing key. Call once at game startup. A null fingerprint leaves the current pin unchanged.</summary>
public static void Configure( string projectId, string apiKey, string baseUrl, string apiVersion, string cdnUrl, string publishTarget, string securityKeyFingerprint )
{
var previousRoot = ApiRoot;
var previousProject = ProjectId;
var previousPin = SecuritySigningKeyPin;
ProjectId = projectId;
ApiKey = apiKey;
if ( !string.IsNullOrEmpty( baseUrl ) ) BaseUrl = baseUrl.TrimEnd( '/' );
if ( !string.IsNullOrEmpty( apiVersion ) ) ApiVersion = apiVersion.Trim( '/' );
CdnUrl = string.IsNullOrEmpty( cdnUrl ) ? null : cdnUrl.TrimEnd( '/' );
PublishTarget = NormalizePublishTarget( publishTarget ?? PublishTarget );
if ( securityKeyFingerprint is not null ) SetSecuritySigningKeyPin( securityKeyFingerprint );
if ( ApiRoot != previousRoot || ProjectId != previousProject || SecuritySigningKeyPin != previousPin )
ResetRuntimeSecurityConfig();
_autoConfigAttempted = true;
InitializeServerCompatibility();
NetworkStorageRevisionHandler.Initialize();
NetworkStorageAnalyticsRuntime.EnsureCreated( "configure" );
if ( NetworkStorageLogConfig.LogConfig )
NetLog.Info( "config", $"NetworkStorage ready — {ApiRoot}" );
}
/// <summary>Override the runtime publish target used for endpoint/storage/value requests.</summary>
public static void SetPublishTarget( string publishTarget )
{
PublishTarget = NormalizePublishTarget( publishTarget );
_runtimeAuthSession = null;
}
private static string NormalizePublishTarget( string publishTarget )
=> string.Equals( publishTarget, "next", StringComparison.OrdinalIgnoreCase ) ? "next" : "live";
/// <summary>Reset the auto-configure guard so AutoConfigure() can retry.</summary>
public static void ResetAutoConfigureFlag()
{
_autoConfigAttempted = false;
}
}