Skip to content

Commit 352da65

Browse files
authored
Merge - v6.1.0
This update addresses a performance issue related to the new script extension sorting introduced in v6.0.0. It also includes a refactor of the *mod_main.gd* initialization to no longer require the `modLoader` parameter in the `_init()` function.
2 parents 26bcc8d + f90f798 commit 352da65

File tree

6 files changed

+55
-89
lines changed

6 files changed

+55
-89
lines changed

.github/workflows/main.yml

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
on:
2-
pull_request:
3-
branches-ignore:
4-
- 'main'
1+
on: [pull_request]
52
jobs:
63
check_dependencies:
74
runs-on: ubuntu-latest

addons/mod_loader/internal/script_extension.gd

+40-57
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,35 @@ extends Reference
77

88
const 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
1311
static 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.
4531
class 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

7773
static 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

addons/mod_loader/mod_loader.gd

+13-2
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,21 @@ func _init_mod(mod: ModData) -> void:
333333
ModLoaderLog.debug("Initialized overwrite script -> %s" % mod_overwrites_path, LOG_NAME)
334334

335335
ModLoaderLog.debug("Loading script from -> %s" % mod_main_path, LOG_NAME)
336-
var mod_main_script := ResourceLoader.load(mod_main_path)
336+
var mod_main_script: GDScript = ResourceLoader.load(mod_main_path)
337337
ModLoaderLog.debug("Loaded script -> %s" % mod_main_script, LOG_NAME)
338338

339-
var mod_main_instance: Node = mod_main_script.new(self)
339+
var argument_found: bool = false
340+
for method in mod_main_script.get_script_method_list():
341+
if method.name == "_init":
342+
if method.args.size() > 0:
343+
argument_found = true
344+
345+
var mod_main_instance: Node
346+
if argument_found:
347+
mod_main_instance = mod_main_script.new(self)
348+
ModLoaderDeprecated.deprecated_message("The mod_main.gd _init argument (modLoader = ModLoader) is deprecated. Remove it from your _init to avoid crashes in the next major version.", "6.1.0")
349+
else:
350+
mod_main_instance = mod_main_script.new()
340351
mod_main_instance.name = mod.manifest.get_mod_id()
341352

342353
ModLoaderStore.saved_mod_mains[mod_main_path] = mod_main_instance

addons/mod_loader/mod_loader_setup.gd

-5
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ const new_global_classes := [
2525
"class": "ModManifest",
2626
"language": "GDScript",
2727
"path": "res://addons/mod_loader/resources/mod_manifest.gd"
28-
}, {
29-
"base": "Resource",
30-
"class": "ScriptExtensionData",
31-
"language": "GDScript",
32-
"path": "res://addons/mod_loader/resources/script_extension_data.gd"
3328
}, {
3429
"base": "Resource",
3530
"class": "ModLoaderCurrentOptions",

addons/mod_loader/mod_loader_store.gd

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extends Node
1212
# Most of these settings should never need to change, aside from the DEBUG_*
1313
# options (which should be `false` when distributing compiled PCKs)
1414

15-
const MODLOADER_VERSION = "6.0.2"
15+
const MODLOADER_VERSION = "6.1.0"
1616

1717
# If true, a complete array of filepaths is stored for each mod. This is
1818
# disabled by default because the operation can be very expensive, but may

addons/mod_loader/resources/script_extension_data.gd

-20
This file was deleted.

0 commit comments

Comments
 (0)