@@ -7,57 +7,35 @@ extends Reference
7
7
8
8
const LOG_NAME := "ModLoader:ScriptExtension"
9
9
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
13
11
static func handle_script_extensions () -> void :
14
- var script_extension_data_array := []
12
+ var extension_paths := []
15
13
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 :
18
17
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
+
38
22
# 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 )
41
25
_reload_vanilla_child_classes_for (script )
42
26
43
27
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.
45
31
class InheritanceSorting :
32
+ var stack_cache := {}
46
33
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 )
61
39
62
40
var last_index : int
63
41
for index in a_stack .size ():
@@ -68,10 +46,28 @@ class InheritanceSorting:
68
46
last_index = index
69
47
70
48
if last_index < b_stack .size ():
71
- # 'a' has a shorter stack
72
49
return true
73
50
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
75
71
76
72
77
73
static func apply_extension (extension_path : String ) -> Script :
@@ -114,19 +110,6 @@ static func apply_extension(extension_path: String) -> Script:
114
110
115
111
return child_script
116
112
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
-
130
113
# Reload all children classes of the vanilla class we just extended
131
114
# Calling reload() the children of an extended class seems to allow them to be extended
132
115
# 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