Skip to content

Commit a84128a

Browse files
authored
feat: script extensions sorter checks load order (#357)
* added a comparator method to sort extensions of the same script added a comparator method to sort extensions of the same script following the load order * added a table to avoid iterating through the load order every comparison * style edits/spacing changes renamed a var, added spacing to keep the style consistency * style: added an empty line * changed get_string_in_between to get_mod_dir * better description for get_mod_dir
1 parent 3c091c5 commit a84128a

File tree

4 files changed

+53
-23
lines changed

4 files changed

+53
-23
lines changed

addons/mod_loader/api/mod.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const LOG_NAME := "ModLoader:Mod"
2424
# Returns: void
2525
static func install_script_extension(child_script_path: String) -> void:
2626

27-
var mod_id: String = ModLoaderUtils.get_string_in_between(child_script_path, "res://mods-unpacked/", "/")
27+
var mod_id: String = _ModLoaderPath.get_mod_dir(child_script_path)
2828
var mod_data: ModData = get_mod_data(mod_id)
2929
if not ModLoaderStore.saved_extension_paths.has(mod_data.manifest.get_mod_id()):
3030
ModLoaderStore.saved_extension_paths[mod_data.manifest.get_mod_id()] = []

addons/mod_loader/internal/mod_loader_utils.gd

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,6 @@ static func _is_valid_global_class_dict(global_class_dict: Dictionary) -> bool:
100100
return true
101101

102102

103-
# Returns the string in between two strings in a provided string
104-
static func get_string_in_between(string: String, initial: String, ending: String) -> String:
105-
var start_index: int = string.find(initial)
106-
if start_index == -1:
107-
ModLoaderLog.error("Initial string not found.", LOG_NAME)
108-
return ""
109-
110-
start_index += initial.length()
111-
112-
var end_index: int = string.find(ending, start_index)
113-
if end_index == -1:
114-
ModLoaderLog.error("Ending string not found.", LOG_NAME)
115-
return ""
116-
117-
var found_string: String = string.substr(start_index, end_index - start_index)
118-
119-
return found_string
120-
121-
122103
# Deprecated
123104
# =============================================================================
124105

addons/mod_loader/internal/path.gd

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,24 @@ static func get_path_to_mod_config_file(mod_id: String, config_name: String) ->
182182
var mod_config_dir := get_path_to_mod_configs_dir(mod_id)
183183

184184
return mod_config_dir.plus_file( config_name + ".json")
185+
186+
187+
# Returns the mod directory name ("some-mod") from a given path (e.g. "res://mods-unpacked/some-mod/extensions/extension.gd")
188+
static func get_mod_dir(path: String) -> String:
189+
var initial = ModLoaderStore.UNPACKED_DIR
190+
var ending = "/"
191+
var start_index: int = path.find(initial)
192+
if start_index == -1:
193+
ModLoaderLog.error("Initial string not found.", LOG_NAME)
194+
return ""
195+
196+
start_index += initial.length()
197+
198+
var end_index: int = path.find(ending, start_index)
199+
if end_index == -1:
200+
ModLoaderLog.error("Ending string not found.", LOG_NAME)
201+
return ""
202+
203+
var found_string: String = path.substr(start_index, end_index - start_index)
204+
205+
return found_string

addons/mod_loader/internal/script_extension.gd

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extends Reference
77

88
const LOG_NAME := "ModLoader:ScriptExtension"
99

10+
1011
# Sort script extensions by inheritance and apply them in order
1112
static func handle_script_extensions() -> void:
1213
var extension_paths := []
@@ -30,7 +31,15 @@ static func handle_script_extensions() -> void:
3031
# a script extending script B if A is an ancestor of B.
3132
class InheritanceSorting:
3233
var stack_cache := {}
33-
34+
# This dictionary's keys are mod_ids and it stores the corresponding position in the load_order
35+
var load_order := {}
36+
var unpacked_dir = _ModLoaderPath.get_unpacked_mods_dir_path()
37+
38+
39+
func _init() -> void:
40+
_populate_load_order_table()
41+
42+
3443
# Comparator function. return true if a should go before b. This may
3544
# enforce conditions beyond the stated inheritance relationship.
3645
func _check_inheritances(extension_a: String, extension_b: String) -> bool:
@@ -45,10 +54,11 @@ class InheritanceSorting:
4554
return a_stack[index] < b_stack[index]
4655
last_index = index
4756

48-
if last_index < b_stack.size():
57+
if last_index < b_stack.size() - 1:
4958
return true
5059

51-
return extension_a < extension_b
60+
return compare_mods_order(extension_a, extension_b)
61+
5262

5363
# Returns a list of scripts representing all the ancestors of the extension
5464
# script with the most recent ancestor last.
@@ -68,6 +78,23 @@ class InheritanceSorting:
6878

6979
stack_cache[extension_path] = stack
7080
return stack
81+
82+
83+
# Secondary comparator function for resolving scripts extending the same vanilla script
84+
# Will return whether a comes before b in the load order
85+
func compare_mods_order(extension_a: String, extension_b: String) -> bool:
86+
var mod_a_id: String = _ModLoaderPath.get_mod_dir(extension_a)
87+
var mod_b_id: String = _ModLoaderPath.get_mod_dir(extension_b)
88+
89+
return load_order[mod_a_id] < load_order[mod_b_id]
90+
91+
92+
# Populate a load order dictionary for faster access and comparison between mod ids
93+
func _populate_load_order_table() -> void:
94+
var mod_index := 0
95+
for mod in ModLoaderStore.mod_load_order:
96+
load_order[mod.dir_name] = mod_index
97+
mod_index += 1
7198

7299

73100
static func apply_extension(extension_path: String) -> Script:
@@ -110,6 +137,7 @@ static func apply_extension(extension_path: String) -> Script:
110137

111138
return child_script
112139

140+
113141
# Reload all children classes of the vanilla class we just extended
114142
# Calling reload() the children of an extended class seems to allow them to be extended
115143
# 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

Comments
 (0)