Files
sbox-public/engine/Sandbox.Engine/Scene/GameObject/GameObject.Prefab.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

211 lines
5.9 KiB
C#

using System.Collections.ObjectModel;
using System.Text.Json.Nodes;
namespace Sandbox;
public partial class GameObject
{
/// <summary>
/// We are cloned from a prefab. Stop that.
/// </summary>
public void BreakFromPrefab()
{
if ( !IsOutermostPrefabInstanceRoot )
return;
PrefabInstanceData.ConvertTopLevelNestedToFullPrefabInstances( this );
ClearPrefabInstance();
}
public void UpdateFromPrefab()
{
if ( !IsPrefabInstance )
return;
if ( !IsPrefabInstanceRoot )
{
Clear();
return;
}
PrefabInstance.UpdateGameObjectFromPrefab( this );
}
/// <summary>
/// Get the GameObject of a prefab from file path
/// </summary>
public static GameObject GetPrefab( string prefabFilePath )
{
var prefabFile = ResourceLibrary.Get<PrefabFile>( prefabFilePath );
if ( prefabFile is null ) return default;
return SceneUtility.GetPrefabScene( prefabFile );
}
public string PrefabInstanceSource
{
get
{
return PrefabInstance?.PrefabSource;
}
}
/// <summary>
/// This GameObject is part of a prefab instance.
/// </summary>
public bool IsPrefabInstance
{
get
{
if ( IsPrefabInstanceRoot ) return true;
if ( Parent is null ) return false;
return Parent.IsPrefabInstance;
}
}
/// <summary>
/// This GameObject is the root of a prefab instance.
/// Returns true for regular instance roots and nested prefab instance roots.
/// </summary>
public bool IsPrefabInstanceRoot => _prefabInstanceData is not null && !IsPrefabCacheSceneRoot;
/// <summary>
/// Get the root of the prefab instance this GameObject is part of.
/// </summary>
internal GameObject PrefabInstanceRoot
{
get
{
return IsPrefabInstanceRoot ? this : Parent?.PrefabInstanceRoot;
}
}
/// <summary>
/// Get the outermost prefab instance root of this GameObject.
/// This is the root of the prefab instance this GameObject is part of, or the root of the prefab instance this GameObject is nested in.
/// </summary>
internal GameObject OutermostPrefabInstanceRoot
{
get
{
Assert.True( IsPrefabInstance );
if ( Parent == null ) return this;
// Case 1: GameObject is inside a prefab but isn't the root
if ( IsPrefabInstance && !IsPrefabInstanceRoot ) return Parent.OutermostPrefabInstanceRoot;
// Case 2: GameObject is a prefab root but is nested inside another prefab
if ( IsPrefabInstanceRoot && Parent.IsPrefabInstance && IsNestedPrefabInstanceRoot ) return Parent.OutermostPrefabInstanceRoot;
// Otherwise, we've found the outermost prefab instance root
return this;
}
}
/// <summary>
/// This GameObject is the root of a prefab instance and is not nested inside another prefab instance.
/// </summary>
internal bool IsOutermostPrefabInstanceRoot => IsPrefabInstanceRoot && OutermostPrefabInstanceRoot == this;
/// <summary>
/// This GameObject is the root of a nested prefab instance.
/// </summary>
internal bool IsNestedPrefabInstanceRoot => IsPrefabInstanceRoot && PrefabInstance.IsNested;
/// <summary>
/// Is this PrefabRoot the root of a prefab cache scene?
/// </summary>
internal bool IsPrefabCacheSceneRoot => this is PrefabCacheScene;
/// <summary>
/// The filename of the map this object is defined in.
/// </summary>
private string MapSource { get; set; }
// This should never have been public (maybe we did it for easy access from the editor?),
// It is super easy to mess stuff up when using this incorrectly, even worse now with the new prefab system.
[Obsolete( "Stop using this, you will most likely mess something up." )]
public void SetPrefabSource( string prefabSource )
{
InitPrefabInstance( prefabSource, false );
}
/// <summary>
/// Initializes the instance data.
/// </summary>
internal void InitPrefabInstance( string prefabSource, bool isNested )
{
if ( string.IsNullOrEmpty( prefabSource ) )
{
_prefabInstanceData = null;
return;
}
// Added 12th Dec 2023
prefabSource = prefabSource.Replace( ".object", ".prefab", StringComparison.OrdinalIgnoreCase );
_prefabInstanceData = new PrefabInstanceData( prefabSource, this, isNested );
}
internal void ClearPrefabInstance()
{
_prefabInstanceData = null;
}
internal void SetMapSource( string mapSource )
{
MapSource = mapSource;
}
internal bool IsMapInstanceRoot => MapSource is not null;
/// <summary>
/// Access point for all prefab instance related data.
/// Can be accessed on both instance root and children contained within the instance.
/// For outermost prefab instances this will contain a patch and guid mappings.
/// For nested prefab instances this will just contain the prefab source.
/// </summary>
internal PrefabInstanceData PrefabInstance
{
get
{
return IsPrefabInstanceRoot ? _prefabInstanceData : Parent?.PrefabInstance;
}
}
PrefabInstanceData _prefabInstanceData = null;
/// <summary>
/// Defines objects within a scene hierarchy we want to track for prefab diffing and patching.
/// </summary>
internal static HashSet<Json.TrackedObjectDefinition> DiffObjectDefinitions =
[
Json.TrackedObjectDefinition.CreatePresenceBasedDefinition(
type: "GameObject",
requiredFields: [JsonKeys.Id, JsonKeys.Children, JsonKeys.Components, JsonKeys.Flags],
idProperty: JsonKeys.Id,
parentType: "GameObject",
allowedAsRoot: true,
ignoredProperties: [ JsonKeys.EditorPrefabInstanceNestedSource, JsonKeys.EditorSkipPrefabBreakOnRefresh ]
),
// Prefab Instances are GameObjects as well but have a different structure (children and components array can be omitted)
Json.TrackedObjectDefinition.CreatePresenceBasedDefinition(
type: "GameObject",
requiredFields: [JsonKeys.Id, JsonKeys.PrefabInstanceSource],
idProperty: JsonKeys.Id,
parentType: "GameObject",
allowedAsRoot: false,
atomic: true
),
Json.TrackedObjectDefinition.CreatePresenceBasedDefinition(
type: "Component",
requiredFields: [Component.JsonKeys.Id, Component.JsonKeys.Type, Component.JsonKeys.Enabled],
idProperty: Component.JsonKeys.Id,
parentType: "GameObject",
allowedAsRoot: true
),
];
}