Park/Buildings/Building.Persistence.cs
using HC3.Persistence;
using System.Text.Json.Nodes;

namespace HC3;

partial class Building : IPrefabSourceComponent<Building.SaveData>
{
	/// <summary>
	/// Hack so we don't forget which prefab this came from when it spawns.
	/// </summary>
	public string PrefabSource { get; private set; }

	public sealed record SaveData( Vector3 Position, float Rotation, TintChannelData RedColor,
		TintChannelData GreenColor,
		TintChannelData BlueColor,
		TintChannelData AlphaColor, JsonObject Metadata = null );

	protected virtual JsonObject GetPersistentMetadata()
	{
		var obj = new JsonObject();
		obj["Title"] = Title;
		obj["Uses"] = Uses;
		return obj;
	}

	protected virtual void SetPersistentMetadata( JsonObject obj )
	{
		if ( obj == null )
			return;

		if ( obj.TryGetPropertyValue( "Title", out var title ) )
		{
			Title = title.GetValue<string>();
		}

		if ( obj.TryGetPropertyValue( "Uses", out var uses ) )
		{
			Uses = uses.GetValue<int>();
		}
	}

	SaveData IPrefabSourceComponent<SaveData>.GetSaveData() =>
		new( GridManager.Instance.Terrain.WorldToGrid( WorldPosition ), WorldRotation.Yaw(), RedColor: RedChannel,
		GreenColor: GreenChannel,
		BlueColor: BlueChannel,
		AlphaColor: AlphaChannel, GetPersistentMetadata() );

	void IPrefabSourceComponent<SaveData>.Spawn( SaveData saveData )
	{
		WorldPosition = GridManager.Instance.Terrain.GridToWorld( saveData.Position );
		WorldRotation = Rotation.FromYaw( saveData.Rotation );

		IsPlaced = true;
		Tags.Add( "building" );
		Tags.Add( "selectable" );

		// Load Metadata
		SetPersistentMetadata( saveData.Metadata );

		RedChannel = saveData.RedColor;
		GreenChannel = saveData.GreenColor;
		BlueChannel = saveData.BlueColor;
		AlphaChannel = saveData.AlphaColor;

		// Apply to tint component if it exists
		var tint = GetTintComponent();
		if ( tint.IsValid() )
		{
			tint.PrimaryColor = RedChannel.Color;
			tint.SecondaryColor = GreenChannel.Color;
			tint.AccentColor = BlueChannel.Color;
			tint.DetailColor = AlphaChannel.Color;
		}

		GameObject.Root.NetworkSpawn();
	}

	protected override void OnAwake()
	{
		if ( GameObject.IsPrefabInstanceRoot )
		{
			PrefabSource = GameObject.PrefabInstanceSource;
		}
	}
}

file sealed class BuildingsSaveData : SpawnedPrefabSaveData<Building, Building.SaveData>
{
	public override string PropertyName => "Buildings";
}