mirror of
https://github.com/Facepunch/sbox-public.git
synced 2025-12-23 22:48:07 -05:00
* Stop generating solutions via -test flag add -generatesolution * Add TestAppSystem remove Application.InitUnitTest Avoids some hacks and also makes sure our tests are as close to a real AppSystem as possible. * Add shutdown unit test shuts down an re-inits the engine * Properly dispose native resources hold by managed during shutdown Should fix a bunch of crashes * Fix filesystem and networking tests * StandaloneTest does proper Game Close * Make sure package tests clean up properly * Make sure menu scene and resources are released on shutdown * Report leaked scenes on shutdown * Ensure DestroyImmediate is not used on scenes * Fix unmounting in unit tests not clearing native refs * Force destroy native resource on ResourceLib Clear
178 lines
4.4 KiB
C#
178 lines
4.4 KiB
C#
namespace Sandbox;
|
|
|
|
|
|
public partial class GameObject : IValid
|
|
{
|
|
bool _destroying;
|
|
bool _destroyed;
|
|
|
|
/// <summary>
|
|
/// True if the GameObject is not destroyed
|
|
/// </summary>
|
|
public virtual bool IsValid => !_destroyed && Scene is not null;
|
|
|
|
/// <summary>
|
|
/// Actually destroy the object and its children. Turn off and destroy components.
|
|
/// </summary>
|
|
private void Term()
|
|
{
|
|
using var batch = CallbackBatch.Batch();
|
|
_destroying = true;
|
|
|
|
// Tell our children we're gonna die, give them a chance to become orphaned
|
|
ForEachChild( "Children", true, c => c.OnParentBeingDestroyed() );
|
|
|
|
Components.ForEach( "OnDestroy", true, c => c.Destroy() );
|
|
ForEachChild( "Children", true, c => c.Term() );
|
|
|
|
CallbackBatch.Add( CommonCallback.Term, TermFinal, this, "Term" );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called on a child GameObject when its parent is being destroyed
|
|
/// </summary>
|
|
private void OnParentBeingDestroyed()
|
|
{
|
|
// don't call if we're shutting the scene down
|
|
if ( Scene.IsDestroyed )
|
|
return;
|
|
|
|
// don't call if we're an editor scene
|
|
if ( Scene.IsEditor )
|
|
return;
|
|
|
|
Components.ForEach( "OnParentDestroy", true, c => c.OnParentDestroyInternal() );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Destroy the object and its children and destroy any components without
|
|
/// invoking any callbacks.
|
|
/// </summary>
|
|
private void TermSilent()
|
|
{
|
|
_destroying = true;
|
|
ForEachChild( "Children", true, c => c.TermSilent() );
|
|
TermFinal();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The last thing ever called.
|
|
/// </summary>
|
|
private void TermFinal()
|
|
{
|
|
if ( _destroyed )
|
|
return;
|
|
|
|
_destroyed = true;
|
|
_net?.SendNetworkDestroy();
|
|
|
|
ClearNetworking();
|
|
Transform.Interpolate = false;
|
|
|
|
Scene.Directory.Remove( this );
|
|
Enabled = false;
|
|
_parent?.RemoveChild( this );
|
|
_parent = null;
|
|
Scene = null;
|
|
|
|
Children.RemoveAll( x => x is null );
|
|
Components.RemoveNull();
|
|
|
|
if ( Components.Count > 0 ) Log.Warning( $"Some components weren't deleted ({Components.Count})" );
|
|
if ( Children.Count > 0 ) Log.Warning( $"Some children weren't deleted ({Children.Count})" );
|
|
|
|
SceneMetrics.GameObjectsDestroyed++;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Destroy this object. Will actually be destroyed at the start of the next frame.
|
|
/// </summary>
|
|
[ActionGraphInclude]
|
|
public virtual void Destroy()
|
|
{
|
|
ThreadSafe.AssertIsMainThread();
|
|
|
|
if ( _destroying )
|
|
return;
|
|
|
|
_destroying = true;
|
|
Scene?.QueueDelete( this );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return true if this object is destroyed. This will also return true if the object is marked to be destroyed soon.
|
|
/// </summary>
|
|
public bool IsDestroyed => _destroying || _destroyed;
|
|
|
|
/// <summary>
|
|
/// Destroy this object immediately. Calling this might cause some problems if functions
|
|
/// are expecting the object to still exist, so it's not always a good idea.
|
|
/// </summary>
|
|
public void DestroyImmediate()
|
|
{
|
|
Assert.False( this is Scene, "Don't call DestroyImmediate on a scene." );
|
|
ThreadSafe.AssertIsMainThread();
|
|
|
|
Term();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove all children
|
|
/// </summary>
|
|
/// <param name="child"></param>
|
|
private void RemoveChild( GameObject child )
|
|
{
|
|
var i = Children.IndexOf( child );
|
|
if ( i < 0 ) return;
|
|
|
|
Children.RemoveAt( i );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Destroy all components and child objects
|
|
/// </summary>
|
|
[ActionGraphInclude]
|
|
public void Clear() => Clear( true );
|
|
|
|
/// <summary>
|
|
/// True if this GameObject should survive a scene load
|
|
/// </summary>
|
|
private bool ShouldSurviveSceneTransition => Flags.Contains( GameObjectFlags.DontDestroyOnLoad );
|
|
|
|
/// <summary>
|
|
/// Destroy all components and child objects
|
|
/// </summary>
|
|
internal virtual void Clear( bool includeSaved )
|
|
{
|
|
// Delete all components (this might create new children)
|
|
Components.ForEach( "OnDestroy", true, c => c.Destroy() );
|
|
|
|
// First delete all of the children
|
|
ForEachChild( "Children", true, c =>
|
|
{
|
|
if ( includeSaved || !c.ShouldSurviveSceneTransition )
|
|
c.Term();
|
|
} );
|
|
|
|
// Destroy any newly created children silently
|
|
ForEachChild( "Children", true, c =>
|
|
{
|
|
if ( includeSaved || !c.ShouldSurviveSceneTransition )
|
|
c.TermSilent();
|
|
} );
|
|
|
|
Components.RemoveNull();
|
|
Children.RemoveAll( x => x is null );
|
|
|
|
Assert.AreEqual( 0, Components.Count, $"{Components.Count} components weren't deleted!" );
|
|
|
|
if ( includeSaved )
|
|
{
|
|
Assert.AreEqual( 0, Children.Count, $"{Children.Count} children weren't deleted!" );
|
|
}
|
|
|
|
// Actually delete the objects
|
|
Scene?.SceneWorld?.DeletePendingObjects();
|
|
}
|
|
}
|