@@ -2,17 +2,31 @@ extends Object
22
33const REQUIRE_EXPLICIT_ADDITION := false
44const 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*)|$))*" )
623
7- static var regex_getter_setter : RegEx
824
925var hashmap := {}
1026
1127
1228func process_begin () -> void :
1329 hashmap .clear ()
14- regex_getter_setter = RegEx .new ()
15- regex_getter_setter .compile ("(.*?[sg]et\\ s*=\\ s*)(\\ w+)(\\ g<1>)?(\\ g<2>)?" )
1630
1731
1832func process_script (path : String ) -> String :
@@ -24,20 +38,26 @@ func process_script(path: String) -> String:
2438 # since the generated methods will fulfill inheritance requirements
2539 var class_prefix := str (hash (path ))
2640 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."
2941
3042 var getters_setters := collect_getters_and_setters (source_code )
3143
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
3648
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
3955
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 :
4161 continue
4262
4363 var type_string := get_return_type_string (method .return )
@@ -75,12 +95,12 @@ func process_script(path: String) -> String:
7595 # including the methods from the scripts it extends,
7696 # which leads to multiple entries in the list if they are overridden by the child script.
7797 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 )
7999 source_code_additions += "\n %s " % mod_loader_hook_string
80100
81101 # if we have some additions to the code, append them at the end
82102 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 ]
84104
85105 return source_code
86106
@@ -110,6 +130,25 @@ static func get_function_parameters(method_name: String, text: String, is_static
110130 if not is_top_level_func (text , result .get_start (), is_static ):
111131 return get_function_parameters (method_name , text , is_static , result .get_end ())
112132
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 :
113152 # Use a stack to match parentheses
114153 var stack := []
115154 var closing_paren_index := opening_paren_index
@@ -125,40 +164,46 @@ static func get_function_parameters(method_name: String, text: String, is_static
125164
126165 # If the stack is not empty, that means there's no matching closing parenthesis
127166 if stack .size () != 0 :
128- return ""
167+ return - 1
129168
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
132170
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 (":" , ": " )
140171
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 )
142174
175+ if not func_def :
176+ return text
143177
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 () )
146180
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 ])
149184
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 ()
152196
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+ )
155201
156202 return text
157203
158204
159205static 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 )
162207
163208 # Search for the function definition
164209 return func_with_whitespace .search (text , offset )
@@ -227,7 +272,7 @@ static func get_previous_line_to(text: String, index: int) -> String:
227272 return text .substr (start_index , end_index - start_index + 1 )
228273
229274
230- static func is_func_moddable (method_start_idx , text ) -> bool :
275+ static func is_func_marked_moddable (method_start_idx , text ) -> bool :
231276 var prevline := get_previous_line_to (text , method_start_idx )
232277
233278 if prevline .contains ("@not-moddable" ):
0 commit comments