rokojori_action_library/Tools/mcp/VariantJson.cs

366 lines
9.4 KiB
C#

#if TOOLS
using Godot;
namespace Rokojori.Tools
{
using Rokojori;
// Two-way mapping between Godot Variant and the addon's own JSON types.
// Sub-resources are referenced by resource_path; an embedded sub-resource with
// no saved path round-trips as a tagged placeholder rather than its real data -
// not needed by the first 4 routes, revisit if a fragment ever needs it.
public static class VariantJson
{
public static JSONData ToJson( Variant v )
{
switch ( v.VariantType )
{
case Variant.Type.Nil:
return new JSONValue();
case Variant.Type.Bool:
return new JSONValue( v.AsBool() );
case Variant.Type.Int:
return new JSONValue( (double) v.AsInt64() );
case Variant.Type.Float:
return new JSONValue( v.AsDouble() );
case Variant.Type.String:
case Variant.Type.StringName:
return new JSONValue( v.AsString() );
case Variant.Type.NodePath:
return new JSONValue( v.AsNodePath().ToString() );
case Variant.Type.Vector2:
return Vector2ToJson( v.AsVector2() );
case Variant.Type.Vector2I:
var v2i = v.AsVector2I();
return Vector2ToJson( new Vector2( v2i.X, v2i.Y ) );
case Variant.Type.Vector3:
return Vector3ToJson( v.AsVector3() );
case Variant.Type.Vector3I:
var v3i = v.AsVector3I();
return Vector3ToJson( new Vector3( v3i.X, v3i.Y, v3i.Z ) );
case Variant.Type.Vector4:
return Vector4ToJson( v.AsVector4() );
case Variant.Type.Color:
return ColorToJson( v.AsColor() );
case Variant.Type.Array:
return ArrayToJson( v.AsGodotArray() );
case Variant.Type.Dictionary:
return DictionaryToJson( v.AsGodotDictionary() );
case Variant.Type.Object:
return ObjectToJson( v.AsGodotObject() );
default:
// Not modeled explicitly (Rid, Callable, Signal, Transform*, Basis, Plane, Quaternion, Projection, Aabb, Rect2...).
// Falls back to a readable string so round-tripping these doesn't crash - just loses write support.
return new JSONValue( v.ToString() );
}
}
public static Variant FromJson( JSONData data, Variant.Type targetType )
{
if ( data == null || data.isNull )
{
return new Variant();
}
switch ( targetType )
{
case Variant.Type.Nil:
// Untyped [Export] Variant field - no declared type to convert against, shape-sniff instead.
return FromJsonInferred( data );
case Variant.Type.Bool:
return data.booleanValue;
case Variant.Type.Int:
return data.longValue;
case Variant.Type.Float:
return data.numberValue;
case Variant.Type.String:
return data.stringValue;
case Variant.Type.StringName:
return new StringName( data.stringValue );
case Variant.Type.NodePath:
return new NodePath( data.stringValue );
case Variant.Type.Vector2:
return JsonToVector2( data.AsObject() );
case Variant.Type.Vector2I:
var v2 = JsonToVector2( data.AsObject() );
return new Vector2I( (int) v2.X, (int) v2.Y );
case Variant.Type.Vector3:
return JsonToVector3( data.AsObject() );
case Variant.Type.Vector3I:
var v3 = JsonToVector3( data.AsObject() );
return new Vector3I( (int) v3.X, (int) v3.Y, (int) v3.Z );
case Variant.Type.Vector4:
return JsonToVector4( data.AsObject() );
case Variant.Type.Color:
return JsonToColor( data.AsObject() );
case Variant.Type.Array:
return JsonToArray( data.AsArray() );
case Variant.Type.Dictionary:
return JsonToDictionary( data.AsObject() );
case Variant.Type.Object:
return JsonToResource( data.AsObject() );
default:
RJLog.Error( "MCP VariantJson: write not supported for Variant type", targetType );
return new Variant();
}
}
// Used for Array/Dictionary element values, which have no declared Variant type of their own -
// shape-sniffs the JSON (keys x/y/z/w, r/g/b/a, __resourcePath) rather than a real type lookup.
public static Variant FromJsonInferred( JSONData data )
{
if ( data == null || data.isNull )
{
return new Variant();
}
if ( data.isBoolean )
{
return data.booleanValue;
}
if ( data.isNumber )
{
return data.numberValue;
}
if ( data.isString )
{
return data.stringValue;
}
if ( data.isArray )
{
return JsonToArray( data.AsArray() );
}
if ( data.isObject )
{
var obj = data.AsObject();
if ( obj.Contains( "__resourcePath" ) )
{
return JsonToResource( obj );
}
if ( obj.Contains( "x" ) && obj.Contains( "y" ) && obj.Contains( "z" ) && obj.Contains( "w" ) )
{
return JsonToVector4( obj );
}
if ( obj.Contains( "r" ) && obj.Contains( "g" ) && obj.Contains( "b" ) )
{
return JsonToColor( obj );
}
if ( obj.Contains( "x" ) && obj.Contains( "y" ) && obj.Contains( "z" ) )
{
return JsonToVector3( obj );
}
if ( obj.Contains( "x" ) && obj.Contains( "y" ) )
{
return JsonToVector2( obj );
}
return JsonToDictionary( obj );
}
return new Variant();
}
public static JSONObject Vector2ToJson( Vector2 v )
{
var o = new JSONObject();
o.Set( "x", (double) v.X );
o.Set( "y", (double) v.Y );
return o;
}
public static JSONObject Vector3ToJson( Vector3 v )
{
var o = new JSONObject();
o.Set( "x", (double) v.X );
o.Set( "y", (double) v.Y );
o.Set( "z", (double) v.Z );
return o;
}
public static JSONObject Vector4ToJson( Vector4 v )
{
var o = new JSONObject();
o.Set( "x", (double) v.X );
o.Set( "y", (double) v.Y );
o.Set( "z", (double) v.Z );
o.Set( "w", (double) v.W );
return o;
}
public static JSONObject ColorToJson( Color c )
{
var o = new JSONObject();
o.Set( "r", (double) c.R );
o.Set( "g", (double) c.G );
o.Set( "b", (double) c.B );
o.Set( "a", (double) c.A );
return o;
}
public static Vector2 JsonToVector2( JSONObject o )
{
if ( o == null ) return Vector2.Zero;
return new Vector2( (float) o.GetNumberOr( "x", 0 ), (float) o.GetNumberOr( "y", 0 ) );
}
public static Vector3 JsonToVector3( JSONObject o )
{
if ( o == null ) return Vector3.Zero;
return new Vector3( (float) o.GetNumberOr( "x", 0 ), (float) o.GetNumberOr( "y", 0 ), (float) o.GetNumberOr( "z", 0 ) );
}
public static Vector4 JsonToVector4( JSONObject o )
{
if ( o == null ) return Vector4.Zero;
return new Vector4( (float) o.GetNumberOr( "x", 0 ), (float) o.GetNumberOr( "y", 0 ), (float) o.GetNumberOr( "z", 0 ), (float) o.GetNumberOr( "w", 0 ) );
}
public static Color JsonToColor( JSONObject o )
{
if ( o == null ) return Colors.White;
return new Color(
(float) o.GetNumberOr( "r", 1 ), (float) o.GetNumberOr( "g", 1 ),
(float) o.GetNumberOr( "b", 1 ), (float) o.GetNumberOr( "a", 1 )
);
}
public static JSONArray ArrayToJson( Godot.Collections.Array array )
{
var result = new JSONArray();
foreach ( var item in array )
{
result.Push( ToJson( item ) );
}
return result;
}
public static Godot.Collections.Array JsonToArray( JSONArray array )
{
var result = new Godot.Collections.Array();
if ( array == null )
{
return result;
}
foreach ( var item in array.values )
{
result.Add( FromJsonInferred( item ) );
}
return result;
}
public static JSONObject DictionaryToJson( Godot.Collections.Dictionary dict )
{
var result = new JSONObject();
foreach ( var key in dict.Keys )
{
result.Set( key.AsString(), ToJson( dict[ key ] ) );
}
return result;
}
public static Godot.Collections.Dictionary JsonToDictionary( JSONObject o )
{
var result = new Godot.Collections.Dictionary();
if ( o == null )
{
return result;
}
for ( var i = 0; i < o.keys.Count; i++ )
{
result[ o.keys[ i ] ] = FromJsonInferred( o.values[ i ] );
}
return result;
}
public static JSONData ObjectToJson( GodotObject obj )
{
if ( obj == null )
{
return new JSONValue();
}
var wrapper = new JSONObject();
if ( obj is Resource resource )
{
if ( ! string.IsNullOrEmpty( resource.ResourcePath ) )
{
wrapper.Set( "__resourcePath", resource.ResourcePath );
}
else
{
wrapper.Set( "__embeddedResourceType", resource.GetClass() );
}
return wrapper;
}
return new JSONValue( obj.ToString() );
}
public static Resource JsonToResource( JSONObject o )
{
if ( o == null || ! o.Contains( "__resourcePath" ) )
{
return null;
}
var path = o.GetString( "__resourcePath" );
return ResourceLoader.Load( path );
}
}
}
#endif