2021-01-26 19:05:31 +01:00
extends Node
2021-04-28 13:26:20 +02:00
# Script first adds nodes to scene, then choose some random nodes and reparents
# them or delete and replace with new ones
2022-01-05 19:35:38 +01:00
# This is not really reproducible, but crashes find by this tool should be quite easy to recreate
2021-04-28 13:26:20 +02:00
## Algorithm
# - Add multiple nodes to scene
# - Set name to each
# - In _process
# - Get random node
# - Detach it from its parent
# - Play with a russian roulette:
# - If node will be deleted, be sure to get list of its all children and then
# replace all with new nodes(change also name) and old remove with queue_free()
# - Get another random node
# - If nodes are the same, add node to root one(cannot set self as self parent) and repeat steps
# - If second node is child of first, add first node to root one(prevents from memory leaks due invalid reparenting)
# - At the end add first random node as child of second
var number_of_nodes : int = 0
# Collected nodes
var collected_nodes : Array = [ ]
# Disabled nodes which won't be used
var disabled_classes : Array = [
" ReflectionProbe " , #GH 45460
]
var debug_enabled : bool = false
2021-03-14 13:54:56 +01:00
func collect ( ) - > void :
2021-04-28 13:26:20 +02:00
var classes : Array = ClassDB . get_class_list ( )
2021-03-14 13:54:56 +01:00
classes . sort ( )
for name_of_class in classes :
2021-04-28 13:26:20 +02:00
if ClassDB . is_parent_class ( name_of_class , " Node " ) :
if name_of_class . find ( " Editor " ) != - 1 : # We don't want to test editor nodes
2021-03-14 13:54:56 +01:00
continue
2021-04-28 13:26:20 +02:00
if disabled_classes . has ( name_of_class ) : # Class is disabled
2021-03-14 13:54:56 +01:00
continue
2021-04-28 13:26:20 +02:00
if ClassDB . can_instance ( name_of_class ) : # Only instantable nodes can be used
2021-03-14 13:54:56 +01:00
collected_nodes . append ( name_of_class )
2021-01-26 19:05:31 +01:00
2021-04-28 13:26:20 +02:00
if debug_enabled :
var to_print : String = " DEBUG: List of classes used in ReparentingDeleting scene: \n "
to_print += " DEBUG: [ "
for index in range ( classes . size ( ) ) :
2022-01-05 19:35:38 +01:00
to_print += ' " ' + classes [ index ] + ' " '
2021-04-28 13:26:20 +02:00
if index != classes . size ( ) - 1 :
to_print += " , "
print ( to_print )
2021-01-26 19:05:31 +01:00
func _ready ( ) - > void :
seed ( 405 )
2021-03-14 13:54:56 +01:00
collect ( )
2021-04-28 13:26:20 +02:00
number_of_nodes = max ( collected_nodes . size ( ) , 200 ) # Use at least all nodes, or more if you want(168 is probably number of all nodes)
for i in range ( number_of_nodes ) :
2021-03-14 13:54:56 +01:00
var index = i
2021-04-28 13:26:20 +02:00
if i > = collected_nodes . size ( ) : # Wrap values
2021-03-14 13:54:56 +01:00
index = i % collected_nodes . size ( )
2021-04-28 13:26:20 +02:00
var child : Node = ClassDB . instance ( collected_nodes [ index ] )
2021-03-14 13:54:56 +01:00
child . set_name ( " Special Node " + str ( i ) )
add_child ( child )
2021-04-28 13:26:20 +02:00
2021-01-26 19:05:31 +01:00
func _process ( delta : float ) - > void :
2021-04-28 13:26:20 +02:00
# assert(Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT) == 0) # Don't work good when instancing more than 1 scene, because node queued for deleting
var choosen_node : Node
var parent_of_node : Node
2021-01-26 19:05:31 +01:00
for i in range ( 5 ) :
2021-04-28 13:26:20 +02:00
var number : String = " Special Node " + str ( randi ( ) % number_of_nodes )
choosen_node = find_node ( number , true , false )
2021-01-26 19:05:31 +01:00
parent_of_node = choosen_node . get_parent ( )
2021-04-28 13:26:20 +02:00
var random_node = find_node ( " Special Node " + str ( randi ( ) % number_of_nodes ) , true , false )
2021-01-26 19:05:31 +01:00
parent_of_node . remove_child ( choosen_node )
2021-04-28 13:26:20 +02:00
if randi ( ) % 6 == 0 : # 16% chance to remove node with children
var names_to_remove : Array = find_all_special_children_names ( choosen_node )
2021-01-26 19:05:31 +01:00
for name_to_remove in names_to_remove :
2021-04-28 13:26:20 +02:00
var node : Node = ClassDB . instance ( collected_nodes [ randi ( ) % collected_nodes . size ( ) ] )
2021-01-26 19:05:31 +01:00
node . set_name ( name_to_remove )
add_child ( node )
choosen_node . queue_free ( )
continue
2021-04-28 13:26:20 +02:00
if choosen_node . find_node ( random_node . get_name ( ) , true , false ) != null : # Cannot set as node parent one of its child
2021-01-26 19:05:31 +01:00
add_child ( choosen_node )
continue
2021-04-28 13:26:20 +02:00
if choosen_node == random_node : # Do not reparent node to self
2021-01-26 19:05:31 +01:00
add_child ( choosen_node )
continue
random_node . add_child ( choosen_node )
2021-04-28 13:26:20 +02:00
# Finds recusivelly all child nodes which will be also removed to be able to add
# exactly same number of nodes in replacement.
func find_all_special_children_names ( node : Node ) - > Array :
var array : Array = [ ]
2021-01-26 19:05:31 +01:00
array . append ( node . get_name ( ) )
for child in node . get_children ( ) :
if child . get_name ( ) . begins_with ( " Special Node " ) :
array . append_array ( find_all_special_children_names ( child ) )
2021-04-28 13:26:20 +02:00
2021-01-26 19:05:31 +01:00
return array