mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 19:38:24 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
679 lines
16 KiB
C#
679 lines
16 KiB
C#
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// Flags to search for Components.
|
|
/// I've named this something generic because I think we can re-use it to search for GameObjects too.
|
|
/// </summary>
|
|
[Flags, Expose]
|
|
public enum FindMode
|
|
{
|
|
/// <summary>
|
|
/// Components that are enabled
|
|
/// </summary>
|
|
Enabled = 1,
|
|
|
|
/// <summary>
|
|
/// Components that are disabled
|
|
/// </summary>
|
|
Disabled = 2,
|
|
|
|
/// <summary>
|
|
/// Components in this object
|
|
/// </summary>
|
|
InSelf = 4,
|
|
|
|
/// <summary>
|
|
/// Components in our parent
|
|
/// </summary>
|
|
InParent = 8,
|
|
|
|
/// <summary>
|
|
/// Components in all ancestors (parent, their parent, their parent, etc)
|
|
/// </summary>
|
|
InAncestors = 16,
|
|
|
|
/// <summary>
|
|
/// Components in our children
|
|
/// </summary>
|
|
InChildren = 32,
|
|
|
|
/// <summary>
|
|
/// Components in all decendants (our children, their children, their children etc)
|
|
/// </summary>
|
|
InDescendants = 64,
|
|
|
|
|
|
EnabledInSelf = Enabled | InSelf,
|
|
EnabledInSelfAndDescendants = Enabled | InSelf | InDescendants,
|
|
EnabledInSelfAndChildren = Enabled | InSelf | InChildren,
|
|
|
|
DisabledInSelf = Disabled | InSelf,
|
|
DisabledInSelfAndDescendants = Disabled | InSelf | InDescendants,
|
|
DisabledInSelfAndChildren = Disabled | InSelf | InChildren,
|
|
|
|
EverythingInSelf = Enabled | InSelf | Disabled,
|
|
EverythingInSelfAndDescendants = Enabled | InSelf | Disabled | InDescendants,
|
|
EverythingInSelfAndChildren = Enabled | InSelf | Disabled | InChildren,
|
|
EverythingInSelfAndParent = Enabled | InSelf | Disabled | InParent,
|
|
EverythingInSelfAndAncestors = Enabled | InSelf | Disabled | InAncestors,
|
|
EverythingInAncestors = Enabled | Disabled | InAncestors,
|
|
EverythingInChildren = Enabled | Disabled | InChildren,
|
|
EverythingInDescendants = Enabled | Disabled | InDescendants,
|
|
}
|
|
|
|
public class ComponentList
|
|
{
|
|
readonly GameObject go;
|
|
|
|
/// <summary>
|
|
/// This is the hard list of components.
|
|
/// This isn't a HashSet because we need the order to stay.
|
|
/// </summary>
|
|
List<Component> _list;
|
|
internal ComponentList( GameObject o )
|
|
{
|
|
go = o;
|
|
_list = new List<Component>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all components, including disabled ones
|
|
/// </summary>
|
|
public IEnumerable<Component> GetAll()
|
|
{
|
|
return _list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hotload has occurred
|
|
/// </summary>
|
|
internal void OnHotload()
|
|
{
|
|
_list.RemoveAll( x => x is null );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a component of this type
|
|
/// </summary>
|
|
public Component Create( TypeDescription type, bool startEnabled = true )
|
|
{
|
|
if ( !type.TargetType.IsAssignableTo( typeof( Component ) ) )
|
|
return null;
|
|
|
|
using var batch = CallbackBatch.Batch();
|
|
|
|
var t = type.Create<Component>();
|
|
t.GameObject = go;
|
|
_list.Add( t );
|
|
|
|
t.Enabled = startEnabled;
|
|
go.OnComponentAdded( t );
|
|
|
|
return t;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a component of this type
|
|
/// </summary>
|
|
public T Create<T>( bool startEnabled = true ) where T : Component, new()
|
|
{
|
|
using var batch = CallbackBatch.Batch();
|
|
var t = new T();
|
|
|
|
t.GameObject = go;
|
|
_list.Add( t );
|
|
|
|
t.InitializeComponent();
|
|
t.Enabled = startEnabled;
|
|
go.OnComponentAdded( t );
|
|
|
|
return t;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a component of this type
|
|
/// </summary>
|
|
internal Component Create( Type type, bool startEnabled = true )
|
|
{
|
|
if ( !type.IsAssignableTo( typeof( Component ) ) )
|
|
return null;
|
|
|
|
using var batch = CallbackBatch.Batch();
|
|
var t = (Component)Activator.CreateInstance( type );
|
|
|
|
t.GameObject = go;
|
|
_list.Add( t );
|
|
|
|
t.InitializeComponent();
|
|
t.Enabled = startEnabled;
|
|
go.OnComponentAdded( t );
|
|
|
|
return t;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a component of this type
|
|
/// </summary>
|
|
public T Get<T>( FindMode search )
|
|
{
|
|
return GetAll<T>( search ).FirstOrDefault();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a component of this type
|
|
/// </summary>
|
|
public Component Get( Type type, FindMode find = FindMode.EnabledInSelf )
|
|
{
|
|
return GetAll( type, find ).FirstOrDefault();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all components of this type
|
|
/// </summary>
|
|
public IEnumerable<Component> GetAll( Type type, FindMode find )
|
|
{
|
|
return GetAll<Component>( find ).Where( x => x.GetType().IsAssignableTo( type ) );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all components
|
|
/// </summary>
|
|
public IEnumerable<Component> GetAll( FindMode find ) => GetAll<Component>( find );
|
|
|
|
/// <summary>
|
|
/// Get a list of components on this game object, optionally recurse when deep is true
|
|
/// </summary>
|
|
public IEnumerable<T> GetAll<T>( FindMode find = FindMode.InSelf | FindMode.Enabled | FindMode.InDescendants )
|
|
{
|
|
if ( go.IsDestroyed ) return Enumerable.Empty<T>();
|
|
|
|
var results = new List<T>( 16 );
|
|
|
|
CollectAll( results, find );
|
|
|
|
return results;
|
|
}
|
|
|
|
// This is an incredibly hot code path, even the slightest change should be verified with benchmarks.
|
|
private void CollectAll<T>( List<T> results, FindMode find )
|
|
{
|
|
bool enabledOnly = find.Contains( FindMode.Enabled );
|
|
bool disabledOnly = find.Contains( FindMode.Disabled );
|
|
|
|
if ( enabledOnly == disabledOnly )
|
|
{
|
|
enabledOnly = false;
|
|
disabledOnly = false;
|
|
}
|
|
|
|
if ( enabledOnly && !go.Enabled ) return;
|
|
|
|
//
|
|
// Find in self
|
|
//
|
|
if ( find.Contains( FindMode.InSelf ) )
|
|
{
|
|
for ( int i = 0; i < _list.Count; i++ )
|
|
{
|
|
var component = _list[i];
|
|
if ( component is null ) continue;
|
|
|
|
if ( enabledOnly && !component.Active ) continue;
|
|
if ( disabledOnly && component.Active ) continue;
|
|
|
|
if ( component is T c )
|
|
{
|
|
results.Add( c );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find in children
|
|
//
|
|
if ( find.Contains( FindMode.InChildren ) || find.Contains( FindMode.InDescendants ) )
|
|
{
|
|
var childFlags = find | FindMode.InSelf;
|
|
childFlags &= ~FindMode.InParent;
|
|
childFlags &= ~FindMode.InAncestors;
|
|
|
|
// If we're not searching all descendants then remove the InChildren flag
|
|
if ( !find.Contains( FindMode.InDescendants ) )
|
|
{
|
|
childFlags &= ~FindMode.InChildren;
|
|
}
|
|
|
|
for ( int i = 0; i < go.Children.Count; i++ )
|
|
{
|
|
var child = go.Children[i];
|
|
if ( child.IsValid() )
|
|
{
|
|
child.Components.CollectAll( results, childFlags );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find in parent
|
|
//
|
|
if ( find.Contains( FindMode.InParent ) || find.Contains( FindMode.InAncestors ) )
|
|
{
|
|
var parentFlags = find | FindMode.InSelf;
|
|
parentFlags &= ~FindMode.InChildren;
|
|
parentFlags &= ~FindMode.InDescendants;
|
|
|
|
// If we're not searching all ancestors then remove the InParent flag
|
|
if ( !find.Contains( FindMode.InAncestors ) )
|
|
{
|
|
parentFlags &= ~FindMode.InParent;
|
|
}
|
|
|
|
if ( go.Parent is not null && go.Parent is PrefabScene or not Scene )
|
|
{
|
|
go.Parent.Components.CollectAll( results, parentFlags );
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void Execute<T>( Action<T> action, FindMode find = FindMode.EnabledInSelfAndDescendants )
|
|
{
|
|
switch ( find )
|
|
{
|
|
// Most common case has a fast path
|
|
case FindMode.EnabledInSelfAndDescendants:
|
|
ExecuteEnabledInSelfAndDescendants( action );
|
|
break;
|
|
default:
|
|
ExecuteGeneric( action, find );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Calling this directly is faster than going through Execute
|
|
internal void ExecuteEnabledInSelfAndDescendants<T>( Action<T> action )
|
|
{
|
|
if ( !go.IsValid() || !go.Enabled )
|
|
return;
|
|
|
|
// Check components on this GameObject
|
|
for ( int i = 0; i < _list.Count; i++ )
|
|
{
|
|
var component = _list[i];
|
|
if ( component is null ) continue;
|
|
|
|
if ( component is T target && component.Active )
|
|
{
|
|
action.Invoke( target );
|
|
}
|
|
}
|
|
// Recurse to children
|
|
for ( int i = go.Children.Count - 1; i >= 0; i-- )
|
|
{
|
|
if ( i >= go.Children.Count )
|
|
continue;
|
|
|
|
var child = go.Children[i];
|
|
if ( !child.IsValid() ) continue;
|
|
|
|
child.Components.ExecuteEnabledInSelfAndDescendants( action );
|
|
}
|
|
}
|
|
|
|
private void ExecuteGeneric<T>( Action<T> action, FindMode find = FindMode.EnabledInSelfAndDescendants )
|
|
{
|
|
bool enabledOnly = find.Contains( FindMode.Enabled );
|
|
bool disabledOnly = find.Contains( FindMode.Disabled );
|
|
|
|
if ( enabledOnly == disabledOnly )
|
|
{
|
|
enabledOnly = false;
|
|
disabledOnly = false;
|
|
}
|
|
|
|
if ( enabledOnly && !go.Enabled ) return;
|
|
|
|
//
|
|
// Execute in self
|
|
//
|
|
if ( find.Contains( FindMode.InSelf ) )
|
|
{
|
|
for ( int i = 0; i < _list.Count; i++ )
|
|
{
|
|
var component = _list[i];
|
|
if ( component is null ) continue;
|
|
|
|
if ( enabledOnly && !component.Active ) continue;
|
|
if ( disabledOnly && component.Active ) continue;
|
|
|
|
if ( component is T target )
|
|
{
|
|
action.Invoke( target );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Execute in children
|
|
//
|
|
if ( find.Contains( FindMode.InChildren ) || find.Contains( FindMode.InDescendants ) )
|
|
{
|
|
var childFlags = find | FindMode.InSelf;
|
|
childFlags &= ~FindMode.InParent;
|
|
childFlags &= ~FindMode.InAncestors;
|
|
|
|
// If we're not searching all descendants then remove the InChildren flag
|
|
if ( !find.Contains( FindMode.InDescendants ) )
|
|
{
|
|
childFlags &= ~FindMode.InChildren;
|
|
}
|
|
|
|
for ( int i = 0; i < go.Children.Count; i++ )
|
|
{
|
|
var child = go.Children[i];
|
|
if ( child.IsValid() )
|
|
{
|
|
child.Components.Execute( action, childFlags );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Execute in parent
|
|
//
|
|
if ( find.Contains( FindMode.InParent ) || find.Contains( FindMode.InAncestors ) )
|
|
{
|
|
var parentFlags = find | FindMode.InSelf;
|
|
parentFlags &= ~FindMode.InChildren;
|
|
parentFlags &= ~FindMode.InDescendants;
|
|
|
|
// If we're not searching all ancestors then remove the InParent flag
|
|
if ( !find.Contains( FindMode.InAncestors ) )
|
|
{
|
|
parentFlags &= ~FindMode.InParent;
|
|
}
|
|
|
|
if ( go.Parent is not null && go.Parent is PrefabScene or not Scene )
|
|
{
|
|
go.Parent.Components.Execute( action, parentFlags );
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to get this component
|
|
/// </summary>
|
|
public bool TryGet<T>( out T component, FindMode search = FindMode.EnabledInSelf )
|
|
{
|
|
component = Get<T>( search );
|
|
|
|
return component is not null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows linq style queries
|
|
/// </summary>
|
|
public Component FirstOrDefault( Func<Component, bool> value ) => _list.FirstOrDefault( value );
|
|
|
|
/// <summary>
|
|
/// Amount of components - including disabled
|
|
/// </summary>
|
|
public int Count => _list.Count;
|
|
|
|
public void ForEach<T>( string name, bool includeDisabled, Action<T> action )
|
|
{
|
|
if ( !includeDisabled && !go.Active )
|
|
return;
|
|
|
|
for ( int i = _list.Count - 1; i >= 0 && i < _list.Count; i-- )
|
|
{
|
|
Component c = _list[i];
|
|
|
|
if ( c is null )
|
|
{
|
|
_list.RemoveAt( i );
|
|
continue;
|
|
}
|
|
|
|
if ( !includeDisabled && !c.Active )
|
|
continue;
|
|
|
|
if ( c is not T t )
|
|
continue;
|
|
|
|
try
|
|
{
|
|
action( t );
|
|
}
|
|
catch ( System.Exception e )
|
|
{
|
|
Log.Warning( e, $"Exception when calling {name} on {c}: {e.Message}" );
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ForEach( string name, bool includeDisabled, Action<Component> action ) => ForEach<Component>( name, includeDisabled, action );
|
|
|
|
internal void RemoveNull()
|
|
{
|
|
_list.RemoveAll( x => x is null );
|
|
}
|
|
|
|
internal void OnDestroyedInternal( Component baseComponent )
|
|
{
|
|
if ( _list.Remove( baseComponent ) )
|
|
{
|
|
go.OnComponentRemoved( baseComponent );
|
|
}
|
|
}
|
|
|
|
internal int IndexOf( Component baseComponent )
|
|
{
|
|
return _list.IndexOf( baseComponent );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move the position of the component in the list by delta (-1 means up one, 1 means down one)
|
|
/// </summary>
|
|
public void Move( Component baseComponent, int delta )
|
|
{
|
|
var i = _list.IndexOf( baseComponent );
|
|
if ( i < 0 ) return;
|
|
|
|
i += delta;
|
|
|
|
if ( i < 0 ) i = 0;
|
|
if ( i >= _list.Count ) i = _list.Count - 1;
|
|
|
|
// Move the element
|
|
_list.RemoveAt( _list.IndexOf( baseComponent ) );
|
|
_list.Insert( i, baseComponent );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move the component to a specific index in the list.
|
|
/// If a component is already at that index, it will be swapped with the component being moved.
|
|
/// </summary>
|
|
internal void MoveToIndex( Component comp, int targetIndex )
|
|
{
|
|
var compIndex = _list.IndexOf( comp );
|
|
|
|
_list[compIndex] = _list[targetIndex];
|
|
_list[targetIndex] = comp;
|
|
}
|
|
|
|
//
|
|
// Easy Modes
|
|
//
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject
|
|
/// </summary>
|
|
public T Get<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InSelf;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find this component, if it doesn't exist - create it.
|
|
/// </summary>
|
|
public T GetOrCreate<T>( FindMode flags = FindMode.EverythingInSelf ) where T : Component, new()
|
|
{
|
|
if ( TryGet<T>( out var component, flags ) )
|
|
return component;
|
|
|
|
return Create<T>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject's ancestors or on self
|
|
/// </summary>
|
|
public T GetInAncestorsOrSelf<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InSelf | FindMode.InAncestors;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject's ancestors
|
|
/// </summary>
|
|
public T GetInAncestors<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InAncestors;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject's decendants or on self
|
|
/// </summary>
|
|
public T GetInDescendantsOrSelf<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InSelf | FindMode.InDescendants;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject's decendants
|
|
/// </summary>
|
|
public T GetInDescendants<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InDescendants;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject's immediate children or on self
|
|
/// </summary>
|
|
public T GetInChildrenOrSelf<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InSelf | FindMode.InChildren;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject's immediate children
|
|
/// </summary>
|
|
public T GetInChildren<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InChildren;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject's parent or on self
|
|
/// </summary>
|
|
public T GetInParentOrSelf<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InSelf | FindMode.InParent;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject's parent
|
|
/// </summary>
|
|
public T GetInParent<T>( bool includeDisabled = false )
|
|
{
|
|
var f = FindMode.InParent;
|
|
if ( !includeDisabled ) f |= FindMode.Enabled;
|
|
|
|
return Get<T>( f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find component on this gameobject with the specified id
|
|
/// </summary>
|
|
public Component Get( Guid id )
|
|
{
|
|
return GetAll().FirstOrDefault( x => x.Id.Equals( id ) );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a special component that will keep information about a missing component.
|
|
/// This component just holds the raw json of this component.
|
|
/// </summary>
|
|
internal void AddMissing( MissingComponent missing )
|
|
{
|
|
missing.GameObject = go;
|
|
_list.Add( missing );
|
|
go.OnComponentAdded( missing );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Interface for types that reference a <see cref="ComponentList"/>, to provide
|
|
/// convenience method for accessing that list.
|
|
/// </summary>
|
|
[Expose, Title( "Component List" ), Icon( "apps" )]
|
|
public interface IComponentLister
|
|
{
|
|
[ActionGraphIgnore]
|
|
ComponentList Components { get; }
|
|
|
|
[Title( "Create {T|Component}" )]
|
|
public T Create<T>( bool startEnabled = true ) where T : Component, new()
|
|
{
|
|
return Components.Create<T>( startEnabled );
|
|
}
|
|
|
|
[Pure, Title( "Get {T|Component}" )]
|
|
public T Get<[HasImplementation( typeof( Component ) )] T>( FindMode search = FindMode.EnabledInSelf )
|
|
{
|
|
return Components.GetAll<T>( search ).FirstOrDefault();
|
|
}
|
|
|
|
[Pure, Title( "Try Get {T|Component}" )]
|
|
public bool TryGet<[HasImplementation( typeof( Component ) )] T>( out T component,
|
|
FindMode search = FindMode.EnabledInSelf )
|
|
{
|
|
return Components.TryGet( out component, search );
|
|
}
|
|
|
|
[Pure, Title( "Get All {T|Component}" )]
|
|
public IEnumerable<T> GetAll<[HasImplementation( typeof( Component ) )] T>( FindMode search = FindMode.EnabledInSelf )
|
|
{
|
|
return Components.GetAll<T>( search );
|
|
}
|
|
|
|
[Title( "Get or Create {T|Component}" )]
|
|
public T GetOrCreate<T>( FindMode flags = FindMode.EverythingInSelf ) where T : Component, new()
|
|
{
|
|
return Components.GetOrCreate<T>( flags );
|
|
}
|
|
}
|