@@ -7,57 +7,35 @@ extends Reference
77
88const LOG_NAME := "ModLoader:ScriptExtension"
99
10-
11- # Couple the extension paths with the parent paths and the extension's mod id
12- # in a ScriptExtensionData resource
10+ # Sort script extensions by inheritance and apply them in order
1311static func handle_script_extensions () -> void :
14- var script_extension_data_array := []
12+ var extension_paths := []
1513 for extension_path in ModLoaderStore .script_extensions :
16-
17- if not File .new ().file_exists (extension_path ):
14+ if File .new ().file_exists (extension_path ):
15+ extension_paths .push_back (extension_path )
16+ else :
1817 ModLoaderLog .error ("The child script path '%s ' does not exist" % [extension_path ], LOG_NAME )
19- continue
20-
21- var child_script = ResourceLoader .load (extension_path )
22-
23- var mod_id : String = extension_path .trim_prefix (_ModLoaderPath .get_unpacked_mods_dir_path ()).get_slice ("/" , 0 )
24-
25- var parent_script : Script = child_script .get_base_script ()
26- var parent_script_path : String = parent_script .resource_path
27-
28- script_extension_data_array .push_back (
29- ScriptExtensionData .new (extension_path , parent_script_path , mod_id )
30- )
31-
32- # Sort the extensions based on dependencies
33- script_extension_data_array = _sort_extensions_from_load_order (script_extension_data_array )
34-
35- # Inheritance is more important so this called last
36- script_extension_data_array .sort_custom (InheritanceSorting , "_check_inheritances" )
37-
18+
19+ # Sort by inheritance
20+ extension_paths .sort_custom (InheritanceSorting .new (), "_check_inheritances" )
21+
3822 # Load and install all extensions
39- for extension in script_extension_data_array :
40- var script : Script = apply_extension (extension . extension_path )
23+ for extension in extension_paths :
24+ var script : Script = apply_extension (extension )
4125 _reload_vanilla_child_classes_for (script )
4226
4327
44- # Inner class so the sort function can be called by handle_script_extensions()
28+ # Sorts script paths by their ancestors. Scripts are organized by their common
29+ # acnestors then sorted such that scripts extending script A will be before
30+ # a script extending script B if A is an ancestor of B.
4531class InheritanceSorting :
32+ var stack_cache := {}
4633
47- static func _check_inheritances (extension_a : ScriptExtensionData , extension_b : ScriptExtensionData ) -> bool :
48- var a_stack := []
49- var parent_script : Script = load (extension_a .extension_path )
50- while parent_script :
51- a_stack .push_front (parent_script .resource_path )
52- parent_script = parent_script .get_base_script ()
53- a_stack .pop_back ()
54-
55- var b_stack := []
56- parent_script = load (extension_b .extension_path )
57- while parent_script :
58- b_stack .push_front (parent_script .resource_path )
59- parent_script = parent_script .get_base_script ()
60- b_stack .pop_back ()
34+ # Comparator function. return true if a should go before b. This may
35+ # enforce conditions beyond the stated inheritance relationship.
36+ func _check_inheritances (extension_a : String , extension_b : String ) -> bool :
37+ var a_stack := cached_inheritances_stack (extension_a )
38+ var b_stack := cached_inheritances_stack (extension_b )
6139
6240 var last_index : int
6341 for index in a_stack .size ():
@@ -68,10 +46,28 @@ class InheritanceSorting:
6846 last_index = index
6947
7048 if last_index < b_stack .size ():
71- # 'a' has a shorter stack
7249 return true
7350
74- return extension_a .extension_path < extension_b .extension_path
51+ return extension_a < extension_b
52+
53+ # Returns a list of scripts representing all the ancestors of the extension
54+ # script with the most recent ancestor last.
55+ #
56+ # Results are stored in a cache keyed by extension path
57+ func cached_inheritances_stack (extension_path : String ) -> Array :
58+ if stack_cache .has (extension_path ):
59+ return stack_cache [extension_path ]
60+
61+ var stack := []
62+
63+ var parent_script : Script = load (extension_path )
64+ while parent_script :
65+ stack .push_front (parent_script .resource_path )
66+ parent_script = parent_script .get_base_script ()
67+ stack .pop_back ()
68+
69+ stack_cache [extension_path ] = stack
70+ return stack
7571
7672
7773static func apply_extension (extension_path : String ) -> Script :
@@ -114,19 +110,6 @@ static func apply_extension(extension_path: String) -> Script:
114110
115111 return child_script
116112
117-
118- # Sort an array of ScriptExtensionData following the load order
119- static func _sort_extensions_from_load_order (extensions : Array ) -> Array :
120- var extensions_sorted := []
121-
122- for _mod_data in ModLoaderStore .mod_load_order :
123- for script in extensions :
124- if script .mod_id == _mod_data .dir_name :
125- extensions_sorted .push_front (script )
126-
127- return extensions_sorted
128-
129-
130113# Reload all children classes of the vanilla class we just extended
131114# Calling reload() the children of an extended class seems to allow them to be extended
132115# e.g if B is a child class of A, reloading B after apply an extender of A allows extenders of B to properly extend B, taking A's extender(s) into account
0 commit comments