using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace Sandbox;
///
/// A GameObject which is saved to a file.
///
[Expose]
[AssetType( Name = "Prefab", Extension = "prefab", Category = "World", Flags = AssetTypeFlags.NoEmbedding )]
public partial class PrefabFile : GameResource
{
///
/// Contains the original JSON read from File.
///
public JsonObject RootObject { get; set; }
public override int ResourceVersion => 2;
///
/// This is used as a reference
///
[JsonIgnore]
internal PrefabCacheScene CachedScene { get; set; }
///
/// Get the actual scene scene
///
public PrefabScene GetScene()
{
if ( CachedScene is not null )
return CachedScene;
CachedScene = new PrefabCacheScene()
{
Source = this,
Name = ResourceName
};
CachedScene.Load( this );
return CachedScene;
}
protected override void PostLoad()
{
PostReload();
}
protected override void PostReload()
{
// Make sure our RootObjects name is consistent with the file name.
// In case of renames or duplicated prefabs.
if ( RootObject is not null && RootObject[GameObject.JsonKeys.Name]?.GetValue() != ResourceName )
{
RootObject[GameObject.JsonKeys.Name] = ResourceName;
}
// Load the cached scene
if ( CachedScene is PrefabCacheScene cachedScene )
{
cachedScene.Refresh( this );
}
Register();
}
protected override void OnDestroy()
{
CachedScene?.DestroyImmediate();
CachedScene = null;
Unregister();
}
///
/// If true then we'll show this in the right click menu, so people can create it
///
public bool ShowInMenu { get; set; }
///
/// If ShowInMenu is true, this is the path in the menu for this prefab
///
public string MenuPath { get; set; }
///
/// Icon to show to the left of the option in the menu
///
[IconName]
public string MenuIcon { get; set; }
///
/// If true then the prefab will not be broken when created as a template
///
public bool DontBreakAsTemplate { get; set; }
[Hide, JsonIgnore]
protected override Type ActionGraphTargetType => null;
[Hide, JsonIgnore]
protected override object ActionGraphTarget => null;
///
/// Read metadata saved using a ISceneMetadata based component, such as SceneInformation
///
public string GetMetadata( string title, string defaultValue = null )
{
if ( RootObject is null ) return defaultValue;
if ( RootObject["__properties"] is not JsonObject properties ) return defaultValue;
if ( properties["Metadata"] is not JsonObject metadata ) return defaultValue;
return metadata.GetPropertyValue( title, defaultValue );
}
protected override Bitmap CreateAssetTypeIcon( int width, int height )
{
var svg = "";
return Bitmap.CreateFromSvgString( svg, width, height );
}
#region ObjectsById
///
/// If this instance is stored in , what's the key?
///
private Guid? _objectDictKey;
///
/// Add this instance to , indexed by Guid. If the Guid has changed,
/// removes the old entry.
///
private void Register()
{
//
// Get the guid of the root object. We'll reference this over the network
// to look up this prefab via GameObjectDirectory.FindByGuid.
//
if ( RootObject.GetPropertyValue( "__guid", null ) is not { } guid )
{
Unregister();
return;
}
if ( ObjectsById.TryGetValue( guid, out var existing ) )
{
if ( existing == this ) return;
existing.Unregister();
}
_objectDictKey = guid;
ObjectsById[guid] = this;
}
///
/// Remove this instance from .
///
private void Unregister()
{
if ( _objectDictKey is not { } guid ) return;
_objectDictKey = null;
if ( ObjectsById.TryGetValue( guid, out var registered ) && registered != this )
{
Log.Warning( $"Another prefab was registered with this Guid: \"{ResourcePath}\" vs \"{registered.ResourcePath}\"" );
return;
}
ObjectsById.Remove( guid );
}
///
/// We store each prefabfile in here indexed by their root object id, allowing
/// us to discuss them over the network, because the net system will be able to
/// look the GameObject up.
///
private static Dictionary ObjectsById { get; } = new();
///
/// We can look up prefabfile by their object guid
///
internal static PrefabFile FindByGuid( Guid guid )
{
return ObjectsById.GetValueOrDefault( guid );
}
#endregion ObjectsById
}
///
/// A prefab variable definition
///
[Expose]
[Obsolete]
public class PrefabVariable
{
///
/// A unique id for this variable. This is what it will be referred to in code.
///
[ReadOnly]
public string Id { get; set; }
///
/// A user friendly title for this variable
///
public string Title { get; set; }
///
/// A user friendly description for this variable
///
[TextArea]
public string Description { get; set; }
///
/// An optional group for this variable to belong to
///
public string Group { get; set; }
///
/// Lower numbers appear first
///
public int Order { get; set; }
///
/// Component variables that are being targetted
///
[Hide]
public List Targets { get; set; }
///
/// Add a target property
///
public void AddTarget( Guid id, string propertyName )
{
Targets.Add( new PrefabVariableTarget { Id = id, Property = propertyName } );
}
///
/// Targets a property in a component or gameobject.
///
/// The Id of the gameobject or component.
/// The name of the parameter on the target.
public record struct PrefabVariableTarget( Guid Id, string Property );
}