@@ -70,6 +70,136 @@ def generate_wrappers(target):
70
70
f .write (txt )
71
71
72
72
73
+ def generate_virtual_version (argcount , const = False , returns = False ):
74
+ s = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
75
+ StringName _gdvirtual_##m_name##_sn = #m_name;\\
76
+ template <bool required>\\
77
+ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\
78
+ if (::godot::internal::gdextension_interface_object_has_script_method(_owner, &_gdvirtual_##m_name##_sn)) { \\
79
+ GDExtensionCallError ce;\\
80
+ $CALLSIARGS\\
81
+ $CALLSIBEGIN::godot::internal::gdextension_interface_object_call_script_method(_owner, &_gdvirtual_##m_name##_sn, $CALLSIARGPASS, $CALLSIRETPASS, &ce);\\
82
+ if (ce.error == GDEXTENSION_CALL_OK) {\\
83
+ $CALLSIRET\\
84
+ return true;\\
85
+ }\\
86
+ }\\
87
+ if (required) {\\
88
+ ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
89
+ $RVOID\\
90
+ }\\
91
+ return false;\\
92
+ }\\
93
+ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\
94
+ return godot::internal::gdextension_interface_object_has_script_method(_owner, &_gdvirtual_##m_name##_sn); \\
95
+ }\\
96
+ _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() {\\
97
+ MethodInfo method_info;\\
98
+ method_info.name = #m_name;\\
99
+ method_info.flags = $METHOD_FLAGS;\\
100
+ $FILL_METHOD_INFO\\
101
+ return method_info;\\
102
+ }
103
+
104
+ """
105
+
106
+ sproto = str (argcount )
107
+ method_info = ""
108
+ if returns :
109
+ sproto += "R"
110
+ s = s .replace ("$RET" , "m_ret," )
111
+ s = s .replace ("$RVOID" , "(void)r_ret;" ) # If required, may lead to uninitialized errors
112
+ method_info += "method_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\ \n "
113
+ method_info += "\t \t method_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;"
114
+ else :
115
+ s = s .replace ("$RET " , "" )
116
+ s = s .replace ("\t \t \t $RVOID\\ \n " , "" )
117
+
118
+ if const :
119
+ sproto += "C"
120
+ s = s .replace ("$CONST" , "const" )
121
+ s = s .replace ("$METHOD_FLAGS" , "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST" )
122
+ else :
123
+ s = s .replace ("$CONST " , "" )
124
+ s = s .replace ("$METHOD_FLAGS" , "METHOD_FLAG_VIRTUAL" )
125
+
126
+ s = s .replace ("$VER" , sproto )
127
+ argtext = ""
128
+ callargtext = ""
129
+ callsiargs = ""
130
+ callsiargptrs = ""
131
+ if argcount > 0 :
132
+ argtext += ", "
133
+ callsiargs = f"Variant vargs[{ argcount } ] = {{ "
134
+ callsiargptrs = f"\t \t \t const Variant *vargptrs[{ argcount } ] = {{ "
135
+ for i in range (argcount ):
136
+ if i > 0 :
137
+ argtext += ", "
138
+ callargtext += ", "
139
+ callsiargs += ", "
140
+ callsiargptrs += ", "
141
+ argtext += f"m_type{ i + 1 } "
142
+ callargtext += f"m_type{ i + 1 } arg{ i + 1 } "
143
+ callsiargs += f"Variant(arg{ i + 1 } )"
144
+ callsiargptrs += f"&vargs[{ i } ]"
145
+ if method_info :
146
+ method_info += "\\ \n \t \t "
147
+ method_info += f"method_info.arguments.push_back(GetTypeInfo<m_type{ i + 1 } >::get_class_info());\\ \n "
148
+ method_info += f"\t \t method_info.arguments_metadata.push_back(GetTypeInfo<m_type{ i + 1 } >::METADATA);"
149
+
150
+ if argcount :
151
+ callsiargs += " };\\ \n "
152
+ callsiargptrs += " };"
153
+ s = s .replace ("$CALLSIARGS" , callsiargs + callsiargptrs )
154
+ s = s .replace ("$CALLSIARGPASS" , f"(const GDExtensionConstVariantPtr *)vargptrs, { argcount } " )
155
+ else :
156
+ s = s .replace ("\t \t \t $CALLSIARGS\\ \n " , "" )
157
+ s = s .replace ("$CALLSIARGPASS" , "nullptr, 0" )
158
+
159
+ if returns :
160
+ if argcount > 0 :
161
+ callargtext += ", "
162
+ callargtext += "m_ret &r_ret"
163
+ s = s .replace ("$CALLSIBEGIN" , "Variant ret;\\ \n \t \t \t " )
164
+ s = s .replace ("$CALLSIRETPASS" , "&ret" )
165
+ s = s .replace ("$CALLSIRET" , "r_ret = VariantCaster<m_ret>::cast(ret);" )
166
+ else :
167
+ s = s .replace ("$CALLSIBEGIN" , "" )
168
+ s = s .replace ("$CALLSIRETPASS" , "nullptr" )
169
+ s = s .replace ("\t \t \t \t $CALLSIRET\\ \n " , "" )
170
+
171
+ s = s .replace (" $ARG" , argtext )
172
+ s = s .replace ("$CALLARGS" , callargtext )
173
+ if method_info :
174
+ s = s .replace ("$FILL_METHOD_INFO" , method_info )
175
+ else :
176
+ s = s .replace ("\t \t $FILL_METHOD_INFO\\ \n " , method_info )
177
+
178
+ return s
179
+
180
+
181
+ def generate_virtuals (target ):
182
+ max_versions = 12
183
+
184
+ txt = """/* THIS FILE IS GENERATED DO NOT EDIT */
185
+ #ifndef GDEXTENSION_GDVIRTUAL_GEN_H
186
+ #define GDEXTENSION_GDVIRTUAL_GEN_H
187
+
188
+ """
189
+
190
+ for i in range (max_versions + 1 ):
191
+ txt += f"/* { i } Arguments */\n \n "
192
+ txt += generate_virtual_version (i , False , False )
193
+ txt += generate_virtual_version (i , False , True )
194
+ txt += generate_virtual_version (i , True , False )
195
+ txt += generate_virtual_version (i , True , True )
196
+
197
+ txt += "#endif // GDEXTENSION_GDVIRTUAL_GEN_H\n "
198
+
199
+ with open (target , "w" , encoding = "utf-8" ) as f :
200
+ f .write (txt )
201
+
202
+
73
203
def get_file_list (api_filepath , output_dir , headers = False , sources = False ):
74
204
api = {}
75
205
files = []
@@ -81,6 +211,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
81
211
source_gen_folder = Path (output_dir ) / "gen" / "src"
82
212
83
213
files .append (str ((core_gen_folder / "ext_wrappers.gen.inc" ).as_posix ()))
214
+ files .append (str ((core_gen_folder / "gdvirtual.gen.inc" ).as_posix ()))
84
215
85
216
for builtin_class in api ["builtin_classes" ]:
86
217
if is_pod_type (builtin_class ["name" ]):
@@ -204,6 +335,7 @@ def generate_builtin_bindings(api, output_dir, build_config):
204
335
source_gen_folder .mkdir (parents = True , exist_ok = True )
205
336
206
337
generate_wrappers (core_gen_folder / "ext_wrappers.gen.inc" )
338
+ generate_virtuals (core_gen_folder / "gdvirtual.gen.inc" )
207
339
208
340
# Store types beforehand.
209
341
for builtin_api in api ["builtin_classes" ]:
0 commit comments