Core/NetworkStorageExtensions.cs
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace Sandbox;
/// <summary>
/// Extension helpers for working with Network Storage JSON responses.
/// Reduces boilerplate when parsing endpoint responses into game objects.
///
/// Usage:
/// var ores = response.ReadDictionary( "ores", prop => (float)prop.GetDouble() );
/// var upgrades = response.ReadStringList( "purchasedUpgrades" );
/// var level = response.Int( "level", 1 );
/// </summary>
public static class NetworkStorageExtensions
{
/// <summary>Get an int from a JsonElement, with fallback.</summary>
public static int Int( this JsonElement el, string key, int fallback = 0 )
=> JsonHelpers.GetInt( el, key, fallback );
/// <summary>Get a float from a JsonElement, with fallback.</summary>
public static float Float( this JsonElement el, string key, float fallback = 0f )
=> JsonHelpers.GetFloat( el, key, fallback );
/// <summary>Get a string from a JsonElement, with fallback.</summary>
public static string Str( this JsonElement el, string key, string fallback = "" )
=> JsonHelpers.GetString( el, key, fallback );
/// <summary>Get a bool from a JsonElement, with fallback.</summary>
public static bool Bool( this JsonElement el, string key, bool fallback = false )
=> JsonHelpers.GetBool( el, key, fallback );
/// <summary>
/// Read a JSON array property as a List of strings.
/// Returns empty list if property missing or wrong type.
/// </summary>
public static List<string> ReadStringList( this JsonElement el, string key )
{
var list = new List<string>();
if ( !el.TryGetProperty( key, out var arr ) || arr.ValueKind != JsonValueKind.Array )
return list;
foreach ( var item in arr.EnumerateArray() )
{
if ( item.ValueKind == JsonValueKind.String )
list.Add( item.GetString() );
}
return list;
}
/// <summary>
/// Read a JSON object property as a Dictionary with a value converter.
/// Useful for ore inventories: response.ReadDictionary("ores", v => (float)v.GetDouble())
/// </summary>
public static Dictionary<string, T> ReadDictionary<T>( this JsonElement el, string key, Func<JsonElement, T> converter )
{
var dict = new Dictionary<string, T>();
if ( !el.TryGetProperty( key, out var obj ) || obj.ValueKind != JsonValueKind.Object )
return dict;
foreach ( var prop in obj.EnumerateObject() )
{
try { dict[prop.Name] = converter( prop.Value ); }
catch { /* skip malformed entries */ }
}
return dict;
}
/// <summary>
/// Read a JSON array property as a List with a row converter.
/// Useful for tables: response.ReadList("rows", row => new OreInfo(row))
/// </summary>
public static List<T> ReadList<T>( this JsonElement el, string key, Func<JsonElement, T> converter )
{
var list = new List<T>();
if ( !el.TryGetProperty( key, out var arr ) || arr.ValueKind != JsonValueKind.Array )
return list;
foreach ( var item in arr.EnumerateArray() )
{
try { list.Add( converter( item ) ); }
catch { /* skip malformed entries */ }
}
return list;
}
/// <summary>
/// Compare local state against a server response and return a list of mismatches.
/// Fields is a dictionary of fieldName → (localValue, serverExtractor).
/// </summary>
public static List<string> FindMismatches( this JsonElement serverData, Dictionary<string, (object Local, Func<JsonElement, object> Extract)> fields )
{
var mismatches = new List<string>();
foreach ( var (name, (local, extract)) in fields )
{
try
{
var remote = extract( serverData );
if ( !Equals( local, remote ) )
mismatches.Add( $"{name}: local={local} server={remote}" );
}
catch { /* field not present */ }
}
return mismatches;
}
}