Core/NetworkStorageAutoConfig.cs
using System;
using System.Text.Json;

namespace Sandbox;

public static partial class NetworkStorage
{
	private static bool _autoConfigAttempted;

	/// <summary>
	/// Auto-configure from network-storage.credentials.json.
	/// Called automatically on first API use.
	/// </summary>
	public static void AutoConfigure()
	{
		if ( _autoConfigAttempted ) return;
		_autoConfigAttempted = true;

		NetworkStorageBootstrap.CheckEditorOnce();

		const string fileName = "network-storage.credentials.json";

		string contents = null;
		string[] candidates = {
			fileName,
			$"/{fileName}",
			$"Assets/{fileName}",
			$"/Assets/{fileName}",
		};

		foreach ( var path in candidates )
		{
			if ( FileSystem.Mounted.FileExists( path ) )
			{
				contents = FileSystem.Mounted.ReadAllText( path );
				break;
			}
		}

		if ( string.IsNullOrEmpty( contents ) )
		{
			// Credentials file not found — try NSConfig constants as fallback.
			// The Sync Tool generates a class with hardcoded ProjectId/PublicKey,
			// which works on all clients (no filesystem dependency).
			if ( TryConfigureFromNSConfig() )
				return;

			if ( NetworkStorageLogConfig.LogConfig )
				Log.Warning( "[NetworkStorage] No network-storage.credentials.json found — run Editor → Network Storage → Setup" );
			return;
		}

		try
		{
			var json = JsonSerializer.Deserialize<JsonElement>( contents );

			var projectId = json.TryGetProperty( "projectId", out var pid ) ? pid.GetString() : null;
			var publicKey = json.TryGetProperty( "publicKey", out var pk ) ? pk.GetString() : null;
			var baseUrl = json.TryGetProperty( "baseUrl", out var bu ) ? bu.GetString() : null;
			var apiVersion = json.TryGetProperty( "apiVersion", out var av ) ? av.GetString() : null;
			var cdnUrl = json.TryGetProperty( "cdnUrl", out var cu ) ? cu.GetString() : null;
			var publishTarget = json.TryGetProperty( "publishTarget", out var pt ) ? pt.GetString() : null;

			// Read proxy setting from credentials — only override when explicitly present.
			// ProxyEnabled defaults to false; credentials can explicitly enable it for local multiplayer testing.
			if ( json.TryGetProperty( "proxyEnabled", out var pe ) )
				ProxyEnabled = pe.GetBoolean();
			if ( json.TryGetProperty( "enableAuthSessions", out var eas ) )
				EnableAuthSessions = eas.ValueKind == JsonValueKind.True;
			if ( json.TryGetProperty( "enableEncryptedRequests", out var eer ) )
				EnableEncryptedRequests = eer.ValueKind == JsonValueKind.True;

			if ( !string.IsNullOrEmpty( projectId ) && !string.IsNullOrEmpty( publicKey ) )
			{
				Configure( projectId, publicKey, baseUrl, apiVersion, cdnUrl, publishTarget );
			}
			else if ( NetworkStorageLogConfig.LogConfig )
			{
				Log.Warning( "[NetworkStorage] credentials.json missing projectId or publicKey" );
			}
		}
		catch ( Exception ex )
		{
			if ( NetworkStorageLogConfig.LogConfig )
				Log.Warning( $"[NetworkStorage] Failed to parse credentials.json: {ex.Message}" );
		}
	}

	/// <summary>
	/// Try to configure from the auto-generated NSConfig class.
	/// This provides a fallback when the credentials file isn't available
	/// (e.g. non-host multiplayer clients, published games).
	/// NSConfig is generated by the Sync Tool with the project's public credentials.
	/// </summary>
	private static bool TryConfigureFromNSConfig()
	{
		try
		{
			var nsConfigType = TypeLibrary.GetType( "NSConfig" );
			if ( nsConfigType is null ) return false;

			var projectId = nsConfigType.GetStaticValue( "ProjectId" ) as string;
			var publicKey = nsConfigType.GetStaticValue( "PublicKey" ) as string;
			var baseUrl = nsConfigType.GetStaticValue( "BaseUrl" ) as string;
			var apiVersion = nsConfigType.GetStaticValue( "ApiVersion" ) as string;
			var publishTarget = nsConfigType.GetStaticValue( "PublishTarget" ) as string;

			if ( !string.IsNullOrEmpty( projectId ) && !string.IsNullOrEmpty( publicKey ) )
			{
				// Read ProxyEnabled from the generated class — this is the authoritative
				// runtime source since editor config files are not mounted in game.
				var proxyVal = nsConfigType.GetStaticValue( "ProxyEnabled" );
				if ( proxyVal is bool proxyBool )
					ProxyEnabled = proxyBool;
				var authSessionsVal = nsConfigType.GetStaticValue( "EnableAuthSessions" );
				if ( authSessionsVal is bool authSessionsBool )
					EnableAuthSessions = authSessionsBool;
				var encryptedRequestsVal = nsConfigType.GetStaticValue( "EnableEncryptedRequests" );
				if ( encryptedRequestsVal is bool encryptedRequestsBool )
					EnableEncryptedRequests = encryptedRequestsBool;

				Configure( projectId, publicKey, baseUrl, apiVersion, publishTarget: publishTarget );
				if ( NetworkStorageLogConfig.LogConfig )
					NetLog.Info( "config", $"Configured from NSConfig constants (ProxyEnabled={ProxyEnabled}, PublishTarget={PublishTarget})" );
				return true;
			}
		}
		catch
		{
			// NSConfig may not exist — that's fine, it's optional
		}

		return false;
	}

}