using System.Text.Json;
using System.Text.Json.Nodes;
namespace Sandbox;
public partial class GameObject
{
///
/// Converts Legacy Prefab Instance Data and variables to a patch.
/// - Root level properties: Transform, Flags, Network... are converted to property overrides
/// - Prefab Variables are converted to component property overrides
/// - Prefab to instance guid lookup is created
///
[Expose, JsonUpgrader( typeof( GameObject ), 1 )]
internal static void Upgrader_v1( JsonObject obj )
{
if ( obj[JsonKeys.PrefabInstanceSource] is JsonValue __Prefab && __Prefab.TryGetValue( out string prefabSource ) )
{
if ( !obj.ContainsKey( JsonKeys.PrefabInstancePatch ) )
{
var prefabFile = ResourceLibrary.Get( prefabSource );
if ( !prefabFile.IsValid() )
{
var name = obj.TryGetPropertyValue( JsonKeys.Name, out var nameNode ) ? nameNode.Deserialize() : null;
Log.Warning( $"GameObject {name ?? "unknown"} Upgrader failed to upgrade prefab instance, prefab '{prefabSource}' couldn't be loaded and is likely missing." );
return;
}
// Convert the existing isntance data into a patch
var instancePatch = ConvertLegacyPrefabInstanceToPatch( obj, prefabFile );
var instanceGuid = obj[JsonKeys.Id].Deserialize();
var prefabScene = (PrefabCacheScene)SceneUtility.GetPrefabScene( prefabFile );
// We wont have a lookup table stored yet so create a new one
var prefabLookup = SceneUtility.CreateUniqueGuidLookup( prefabScene.FullPrefabInstanceJson, instanceGuid );
// It's easier to clear the object and start from scratch
obj.Clear();
obj[JsonKeys.PrefabInstanceSource] = JsonValue.Create( prefabSource );
obj[JsonKeys.Id] = instanceGuid;
obj[JsonKeys.PrefabInstancePatch] = Json.ToNode( instancePatch );
obj[JsonKeys.PrefabIdToInstanceId] = Json.ToNode( prefabLookup );
}
}
}
///
/// Backwards combatibility for old prefab instances.
///
private static Json.Patch ConvertLegacyPrefabInstanceToPatch( JsonObject legacyData, PrefabFile prefabFile )
{
var instancePatch = new Json.Patch();
var prefabScene = SceneUtility.GetPrefabScene( prefabFile );
var variables = legacyData[JsonKeys.PrefabInstanceVariables] as JsonObject;
if ( variables is not null )
{
foreach ( (string name, JsonNode value) in variables )
{
#pragma warning disable CS0612
var variable = prefabScene.Variables.Where( x => x.Id == name ).FirstOrDefault();
#pragma warning restore CS0612
if ( variable is null )
{
Log.Warning( $"Prefab Variable not in prefab: {name}" );
continue;
}
foreach ( var target in variable.Targets )
{
instancePatch.PropertyOverrides.Add( new Json.PropertyOverride
{
Target = new Json.ObjectIdentifier
{
Type = "Component", // we only ever support components!
IdValue = target.Id.ToString()
},
Property = target.Property,
Value = value
} );
}
}
}
void TryAddOverrideTargetingPrefab( JsonObject legacyData, string propertyName, JsonNode defaultValue = null )
{
if ( !legacyData.TryGetPropertyValue( propertyName, out var propertyNode ) && defaultValue is null ) return;
instancePatch.PropertyOverrides.Add( new Json.PropertyOverride
{
Target = new Json.ObjectIdentifier
{
Type = "GameObject",
IdValue = prefabScene.Id.ToString()
},
Property = propertyName,
Value = propertyNode ?? defaultValue
} );
}
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.Position, Json.ToNode( Vector3.Zero ) );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.Rotation, Json.ToNode( Rotation.Identity ) );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.Scale, Json.ToNode( Vector3.One ) );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.Name );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.Enabled, false );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.Flags, 0 );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.Tags, prefabFile.RootObject[JsonKeys.Tags] ?? "" );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.NetworkMode, prefabFile.RootObject[JsonKeys.NetworkMode] ?? (int)NetworkMode.Snapshot );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.AlwaysTransmit, prefabFile.RootObject[JsonKeys.AlwaysTransmit] ?? true );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.NetworkOrphaned, prefabFile.RootObject[JsonKeys.NetworkOrphaned] ?? (int)NetworkOrphaned.Destroy );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.OwnerTransfer, prefabFile.RootObject[JsonKeys.OwnerTransfer] ?? (int)OwnerTransfer.Fixed );
TryAddOverrideTargetingPrefab( legacyData, JsonKeys.NetworkInterpolation, prefabFile.RootObject[JsonKeys.NetworkInterpolation] ?? false );
return instancePatch;
}
}