@@ -2,17 +2,31 @@ extends Object
2
2
3
3
const REQUIRE_EXPLICIT_ADDITION := false
4
4
const METHOD_PREFIX := "vanilla_"
5
- const HASH_COLLISION_ERROR := "MODDING EXPORT ERROR: Hash collision between %s and %s . The collision can be resolved by renaming one of the methods or changing their script's path."
5
+ const HASH_COLLISION_ERROR := \
6
+ "MODDING EXPORT ERROR: Hash collision between %s and %s . The collision can be resolved by renaming one of the methods or changing their script's path."
7
+ const MOD_LOADER_HOOKS_START_STRING := \
8
+ "\n # ModLoader Hooks - The following code has been automatically added by the Godot Mod Loader export plugin."
9
+
10
+
11
+ ## finds function names used as setters and getters (excluding inline definitions)
12
+ ## group 2 and 4 contain the xetter names
13
+ static var regex_getter_setter := RegEx .create_from_string ("(.*?[sg]et\\ s*=\\ s*)(\\ w+)(\\ g<1>)?(\\ g<2>)?" )
14
+
15
+ ## finds every instance where super() is called
16
+ ## returns only the super word, excluding the (, as match to make substitution easier
17
+ static var regex_super_call := RegEx .create_from_string ("\\ bsuper(?=\\ s*\\ ()" )
18
+
19
+ ## matches the indented function body
20
+ ## needs to start from the : of a function definition to work (offset)
21
+ ## the body of a function is every line that is empty or starts with an indent or comment
22
+ static var regex_func_body := RegEx .create_from_string ("(?smn)\\ N*(\\ n^(([\\ t #]+\\ N*)|$))*" )
6
23
7
- static var regex_getter_setter : RegEx
8
24
9
25
var hashmap := {}
10
26
11
27
12
28
func process_begin () -> void :
13
29
hashmap .clear ()
14
- regex_getter_setter = RegEx .new ()
15
- regex_getter_setter .compile ("(.*?[sg]et\\ s*=\\ s*)(\\ w+)(\\ g<1>)?(\\ g<2>)?" )
16
30
17
31
18
32
func process_script (path : String ) -> String :
@@ -24,20 +38,26 @@ func process_script(path: String) -> String:
24
38
# since the generated methods will fulfill inheritance requirements
25
39
var class_prefix := str (hash (path ))
26
40
var method_store : Array [String ] = []
27
- var mod_loader_hooks_start_string := \
28
- "\n # ModLoader Hooks - The following code has been automatically added by the Godot Mod Loader export plugin."
29
41
30
42
var getters_setters := collect_getters_and_setters (source_code )
31
43
32
- for method in current_script .get_script_method_list ():
33
- var method_first_line_start := get_index_at_method_start (method . name , source_code )
34
- if method_first_line_start == - 1 or method .name in method_store :
35
- continue
44
+ var moddable_methods := current_script .get_script_method_list (). filter (
45
+ func is_func_moddable (method : Dictionary ):
46
+ if getters_setters . has ( method .name ) :
47
+ return false
36
48
37
- if getters_setters .has (method .name ):
38
- continue
49
+ var method_first_line_start := get_index_at_method_start (method .name , source_code )
50
+ if method_first_line_start == - 1 :
51
+ return false
52
+
53
+ if not is_func_marked_moddable (method_first_line_start , source_code ):
54
+ return false
39
55
40
- if not is_func_moddable (method_first_line_start , source_code ):
56
+ return true
57
+ )
58
+
59
+ for method in moddable_methods :
60
+ if method .name in method_store :
41
61
continue
42
62
43
63
var type_string := get_return_type_string (method .return )
@@ -75,12 +95,12 @@ func process_script(path: String) -> String:
75
95
# including the methods from the scripts it extends,
76
96
# which leads to multiple entries in the list if they are overridden by the child script.
77
97
method_store .push_back (method .name )
78
- source_code = prefix_method_name (method .name , is_static , source_code , METHOD_PREFIX + class_prefix )
98
+ source_code = edit_vanilla_method (method .name , is_static , source_code , METHOD_PREFIX + class_prefix )
79
99
source_code_additions += "\n %s " % mod_loader_hook_string
80
100
81
101
# if we have some additions to the code, append them at the end
82
102
if source_code_additions != "" :
83
- source_code = "%s \n %s \n %s " % [source_code ,mod_loader_hooks_start_string , source_code_additions ]
103
+ source_code = "%s \n %s \n %s " % [source_code , MOD_LOADER_HOOKS_START_STRING , source_code_additions ]
84
104
85
105
return source_code
86
106
@@ -110,6 +130,25 @@ static func get_function_parameters(method_name: String, text: String, is_static
110
130
if not is_top_level_func (text , result .get_start (), is_static ):
111
131
return get_function_parameters (method_name , text , is_static , result .get_end ())
112
132
133
+ var closing_paren_index := get_closing_paren_index (opening_paren_index , text )
134
+ if closing_paren_index == - 1 :
135
+ return ""
136
+
137
+ # Extract the substring between the parentheses
138
+ var param_string := text .substr (opening_paren_index + 1 , closing_paren_index - opening_paren_index - 1 )
139
+
140
+ # Clean whitespace characters (spaces, newlines, tabs)
141
+ param_string = param_string .strip_edges ()\
142
+ .replace (" " , "" )\
143
+ .replace ("\n " , "" )\
144
+ .replace ("\t " , "" )\
145
+ .replace ("," , ", " )\
146
+ .replace (":" , ": " )
147
+
148
+ return param_string
149
+
150
+
151
+ static func get_closing_paren_index (opening_paren_index : int , text : String ) -> int :
113
152
# Use a stack to match parentheses
114
153
var stack := []
115
154
var closing_paren_index := opening_paren_index
@@ -125,40 +164,46 @@ static func get_function_parameters(method_name: String, text: String, is_static
125
164
126
165
# If the stack is not empty, that means there's no matching closing parenthesis
127
166
if stack .size () != 0 :
128
- return ""
167
+ return - 1
129
168
130
- # Extract the substring between the parentheses
131
- var param_string := text .substr (opening_paren_index + 1 , closing_paren_index - opening_paren_index - 1 )
169
+ return closing_paren_index
132
170
133
- # Clean whitespace characters (spaces, newlines, tabs)
134
- param_string = param_string .strip_edges ()\
135
- .replace (" " , "" )\
136
- .replace ("\n " , "" )\
137
- .replace ("\t " , "" )\
138
- .replace ("," , ", " )\
139
- .replace (":" , ": " )
140
171
141
- return param_string
172
+ static func edit_vanilla_method (method_name : String , is_static : bool , text : String , prefix := METHOD_PREFIX , offset := 0 ) -> String :
173
+ var func_def := match_func_with_whitespace (method_name , text , offset )
142
174
175
+ if not func_def :
176
+ return text
143
177
144
- static func prefix_method_name ( method_name : String , is_static : bool , text : String , prefix : = METHOD_PREFIX , offset : = 0 ) -> String :
145
- var result := match_func_with_whitespace (method_name , text , offset )
178
+ if not is_top_level_func ( text , func_def . get_start (), is_static ) :
179
+ return edit_vanilla_method (method_name , is_static , text , prefix , func_def . get_end () )
146
180
147
- if not result :
148
- return text
181
+ text = fix_method_super (method_name , func_def .get_end (), text )
182
+ text = text .erase (func_def .get_start (), func_def .get_end () - func_def .get_start ())
183
+ text = text .insert (func_def .get_start (), "func %s _%s (" % [prefix , method_name ])
149
184
150
- if not is_top_level_func (text , result .get_start (), is_static ):
151
- return prefix_method_name (method_name , is_static , text , prefix , result .get_end ())
185
+ return text
186
+
187
+
188
+ static func fix_method_super (method_name : String , func_def_end : int , text : String , offset := 0 ) -> String :
189
+ var closing_paren_index := get_closing_paren_index (func_def_end , text )
190
+ var func_body_start_index := text .find (":" , closing_paren_index ) + 1
191
+
192
+ var func_body := regex_func_body .search (text , func_body_start_index )
193
+ if not func_body :
194
+ return text
195
+ var func_body_end_index := func_body .get_end ()
152
196
153
- text = text .erase (result .get_start (), result .get_end () - result .get_start ())
154
- text = text .insert (result .get_start (), "func %s _%s (" % [prefix , method_name ])
197
+ text = regex_super_call .sub (
198
+ text , "super.%s " % method_name ,
199
+ true , func_body_start_index , func_body_end_index
200
+ )
155
201
156
202
return text
157
203
158
204
159
205
static func match_func_with_whitespace (method_name : String , text : String , offset := 0 ) -> RegExMatch :
160
- var func_with_whitespace := RegEx .new ()
161
- func_with_whitespace .compile ("func\\ s+%s \\ s*\\ (" % method_name )
206
+ var func_with_whitespace := RegEx .create_from_string ("func\\ s+%s \\ s*\\ (" % method_name )
162
207
163
208
# Search for the function definition
164
209
return func_with_whitespace .search (text , offset )
@@ -227,7 +272,7 @@ static func get_previous_line_to(text: String, index: int) -> String:
227
272
return text .substr (start_index , end_index - start_index + 1 )
228
273
229
274
230
- static func is_func_moddable (method_start_idx , text ) -> bool :
275
+ static func is_func_marked_moddable (method_start_idx , text ) -> bool :
231
276
var prevline := get_previous_line_to (text , method_start_idx )
232
277
233
278
if prevline .contains ("@not-moddable" ):
0 commit comments