1 /++
2 Initialization, termination, and registration of D libraries in Godot
3 +/
4 module godot.api.register;
5 
6 import godot.util.tools.classes;
7 
8 import std.format;
9 import std.meta, std.traits;
10 import std.experimental.allocator, std.experimental.allocator.mallocator;
11 import core.stdc.stdlib : malloc, free;
12 
13 import godot.api.traits;
14 import godot.api.script;
15 import godot.api.wrap;
16 import godot.api.udas;
17 import godot.api.reference;
18 
19 import godot, godot.abi;
20 
21 import godot.abi.gdextension;
22 
23 // global instance for current library
24 __gshared GDNativeExtensionClassLibraryPtr _GODOT_library;
25 
26 enum bool is_(alias a) = is(a);
27 template fileClassesAsLazyImports(FileInfo f) {
28     template classFrom(string className) {
29         mixin(
30             "alias classFrom = from!\"" ~ f.moduleName ~ "\"" ~ className[f.moduleName.length .. $] ~ ";");
31     }
32 
33     alias fileClassesAsLazyImports = staticMap!(classFrom, aliasSeqOf!(f.classes));
34 }
35 
36 /++
37 Pass to GodotNativeLibrary to control D runtime initialization/termination.
38 Default is `yes` unless compiling with BetterC.
39 +/
40 enum LoadDRuntime : bool {
41     no = false,
42     yes = true
43 }
44 
45 /++
46 This mixin will generate the GDNative C interface functions for this D library.
47 Pass to it a name string for the library, followed by the GodotScript types to
48 register, functions to call, and other options to configure Godot-D.
49 
50 The symbolPrefix must match the GDNativeLibrary's symbolPrefix in Godot.
51 
52 D runtime will be initialized and terminated, unless you pass $(D LoadDRuntime.no)
53 or compile with BetterC.
54 
55 Functions taking GodotInitOptions or no arguments will be called at init.
56 Functions taking GodotTerminateOptions will be called at termination.
57 
58 Example:
59 ---
60 import godot, godot.node;
61 class TestClass : GodotScript!Node
62 { }
63 mixin GodotNativeLibrary!(
64 	"testlib",
65 	TestClass,
66 	(GodotInitOptions o){ print("Initialized"); },
67 	(GodotTerminateOptions o){ print("Terminated"); }
68 );
69 ---
70 +/
71 mixin template GodotNativeLibrary(string symbolPrefix, Args...) {
72     private static import godot.abi;
73 
74     private static import godot.abi.gdextension;
75 
76     private static import godot.util.tools.classes;
77     private import godot.api.reference;
78 
79     static if (__traits(compiles, import("classes.csv"))) {
80         enum godotutil.classes.ProjectInfo _GODOT_projectInfo = godotutil.classes.ProjectInfo.fromCsv(
81                 import("classes.csv"));
82     } else {
83         enum godotutil.classes.ProjectInfo _GODOT_projectInfo = godotutil.classes.ProjectInfo.init;
84     }
85 
86     /// HACK: empty main to force the compiler to add emulated TLS.
87     version (Android) void main() {
88     }
89 
90     // Windows DLL entry points handle TLS+DRuntime initialization and thread attachment
91     version (Windows) {
92         version (D_BetterC) {
93         } else {
94             import core.sys.windows.dll : SimpleDllMain;
95 
96             mixin SimpleDllMain;
97         }
98     }
99 
100     /// This is the main entry point declared in your .gdextension file, it will be called by godot engine on load
101     pragma(mangle, symbolPrefix ~ "_gdextension_entry")
102     export extern (C) static GDNativeBool godot_gdextension_entry(GDNativeInterface* p_interface,
103         GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization* r_initialization) {
104         import godot.abi.gdextension;
105         import godot.api.reference;
106         import std.meta, std.traits;
107         import core.runtime : Runtime;
108         import godot.api.output;
109         import godot.api.traits;
110         static import godot.api.register;
111 
112         version (Windows) {
113         } else {
114             version (D_BetterC)
115                 enum bool loadDRuntime = staticIndexOf!(LoadDRuntime.yes, Args) != -1;
116             else
117                 enum bool loadDRuntime = staticIndexOf!(LoadDRuntime.no, Args) == -1;
118             static if (loadDRuntime)
119                 Runtime.initialize();
120         }
121 
122         _godot_api = p_interface;
123         godot.api.register._GODOT_library = p_library;
124 
125         //import core.exception : assertHandler;
126         //assertHandler = (options.in_editor) ? (&godotAssertHandlerEditorDebug)
127         //	: (&godotAssertHandlerCrash);
128 
129         // TODO: explore various stages, for example for making core classes
130         r_initialization.minimum_initialization_level = GDNATIVE_INITIALIZATION_SCENE;
131         r_initialization.initialize = &initializeLevel;
132         r_initialization.deinitialize = &deinitializeLevel;
133 
134         foreach (Arg; Args) {
135             static if (is(Arg)) {
136             }  // is type
137             else static if (isCallable!Arg) {
138                 static if (is(typeof(Arg())))
139                     Arg();
140                 else static if (is(typeof(Arg(options))))
141                     Arg(options);
142             } else static if (is(typeof(Arg) == LoadDRuntime)) {
143             } else {
144                 static assert(0, "Unrecognized argument <" ~ Arg.stringof ~ "> passed to GodotNativeLibrary");
145             }
146         }
147 
148         return 1; // return OK
149     }
150 
151     extern (C) void initializeLevel(void* userdata, GDNativeInitializationLevel level) //@nogc nothrow
152     {
153         //writeln("Initializing level: ", level);
154         import std.exception;
155 
156         register_types(userdata, level);
157     }
158 
159     extern (C) void deinitializeLevel(void* userdata, GDNativeInitializationLevel level) //@nogc nothrow
160     {
161         //writeln("Deinitializing level: ", level);
162     }
163 
164     static void register_types(void* userdata, GDNativeInitializationLevel level) //@nogc nothrow
165     {
166         import std.meta, std.traits;
167         import godot.api.register : register, fileClassesAsLazyImports;
168         import std.array : join;
169         import godot.api.output;
170         import godot.api.traits;
171 
172         // currently only scene-level scripts supportes
173         if (level != GDNATIVE_INITIALIZATION_SCENE)
174             return;
175 
176         alias classList = staticMap!(fileClassesAsLazyImports, aliasSeqOf!(_GODOT_projectInfo.files));
177         static foreach (C; NoDuplicates!(classList, Filter!(is_, Args))) {
178             static if (is(C)) {
179                 static if (extendsGodotBaseClass!C) {
180                     register!C(_GODOT_library);
181                 }
182             }
183         }
184     }
185 
186     /*
187 	pragma(mangle, symbolPrefix~"gdnative_terminate")
188 	export extern(C) static void godot_gdnative_terminate(godot.abi.godot_gdnative_terminate_options* options)
189 	{
190 		import std.meta, std.traits;
191 		import godot.api.script : NativeScriptTemplate;
192 		import std.array : join;
193 		import godot.api.output;
194 		import godot.api.traits;
195 
196 		alias classList = staticMap!(fileClassesAsLazyImports, aliasSeqOf!(_GODOT_projectInfo.files));
197 		static foreach(C; NoDuplicates!(classList, Filter!(is_, Args)))
198 		{
199 			static if(is(C))
200 			{
201 				static if(extendsGodotBaseClass!C)
202 				{
203 					NativeScriptTemplate!C.unref();
204 				}
205 			}
206 		}
207 		
208 		foreach(Arg; Args)
209 		{
210 			static if(is(Arg)) // is type
211 			{
212 			}
213 			else static if(isCallable!Arg)
214 			{
215 				static if(is(typeof(Arg(options)))) Arg(options);
216 			}
217 			else static if(is(typeof(Arg) == LoadDRuntime)) { }
218 			else
219 			{
220 				static assert(0, "Unrecognized argument <"~Arg.stringof~"> passed to GodotNativeLibrary");
221 			}
222 		}
223 		
224 		_GODOT_library.unref();
225 
226 		version(Windows) {}
227 		else
228 		{
229 			import core.runtime : Runtime;
230 			version(D_BetterC) enum bool loadDRuntime = staticIndexOf!(LoadDRuntime.yes, Args) != -1;
231 			else enum bool loadDRuntime = staticIndexOf!(LoadDRuntime.no, Args) == -1;
232 			static if(loadDRuntime) Runtime.terminate();
233 		}
234 	}
235 	*/
236 }
237 
238 private extern (C)
239 godot_variant _GODOT_nop(godot_object o, void* methodData,
240     void* userData, int numArgs, godot_variant** args) {
241     godot_variant n;
242     _godot_api.variant_new_nil(&n);
243     return n;
244 }
245 
246 /++
247 Register a class and all its $(D @GodotMethod) member functions into Godot.
248 +/
249 void register(T)(GDNativeExtensionClassLibraryPtr lib) if (is(T == class)) {
250     import std.array;
251     import godot.abi;
252     import godot.object, godot.resource;
253     import godot.api;
254 
255     //static import godot.nativescript;
256 
257     static if (BaseClassesTuple!T.length == 2) // base class is GodotScript; use owner
258     {
259             alias Base = typeof(T.owner);
260             alias baseName = Base._GODOT_internal_name;
261         }
262     else // base class is another D script
263     {
264             alias Base = BaseClassesTuple!T[0];
265             static if (hasUDA!(Base, Rename))
266                 enum immutable(char*) baseName = godotName!Base ~ '\0';
267             else
268                 enum immutable(char*) baseName = __traits(identifier, Base) ~ '\0';
269         }
270 
271     static if (hasUDA!(T, Rename))
272         enum immutable(char*) name = godotName!T ~ '\0';
273     else
274         enum immutable(char*) name = __traits(identifier, T) ~ '\0';
275     enum fqn = fullyQualifiedName!T ~ '\0';
276 
277     __gshared static GDNativeExtensionClassCreationInfo class_info;
278     class_info.create_instance_func = &createFunc!T;
279     class_info.free_instance_func = &destroyFunc!T;
280     class_info.class_userdata = cast(void*) cast(const char*) name;
281 
282     extern (C) static GDNativeExtensionClassCallVirtual getVirtualFn(void* p_userdata, const char* p_name) {
283         import core.stdc.stdio;
284         import core.stdc.string;
285 
286         //printf("requested method %s\n", p_name);
287         static if (__traits(compiles, __traits(getMember, T, "_ready")))
288             if (strcmp(p_name, "_ready") == 0) {
289                 //puts("onready method found");
290                 return cast(GDNativeExtensionClassCallVirtual)&OnReadyWrapper!(T, __traits(getMember, T, "_ready"))
291                     .callOnReady;
292             }
293         return VirtualMethodsHelper!T.findVCall(cast(string) p_name[0 .. p_name.strlen]);
294     }
295 
296     class_info.get_virtual_func = &getVirtualFn;
297 
298     _godot_api.classdb_register_extension_class(lib, name, baseName, &class_info);
299 
300     void registerMethod(alias mf, string nameOverride = null)() {
301         static if (nameOverride.length) {
302             char[nameOverride.length + 1] mfn = void;
303             mfn[0 .. nameOverride.length] = nameOverride;
304             mfn[$ - 1] = '\0';
305         } else {
306             char[godotName!mf.length + 1] mfn = void;
307             mfn[0 .. godotName!mf.length] = godotName!mf[];
308             mfn[$ - 1] = '\0';
309         }
310 
311         uint flags = GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT;
312 
313         // virtual methods like '_ready'
314         if (__traits(identifier, mf)[0] == '_')
315             flags |= GDNATIVE_EXTENSION_METHOD_FLAG_VIRTUAL;
316 
317         enum isOnReady = godotName!mf == "_ready" && onReadyFieldNames!T.length;
318 
319         GDNativeExtensionClassMethodInfo mi = {
320             mfn.ptr, //const char *name;
321                 &mf, //void *method_userdata;
322                 &MethodWrapper!(T, mf).callMethod, //GDNativeExtensionClassMethodCall call_func;
323                 &MethodWrapper!(T, mf).callPtrMethod, //GDNativeExtensionClassMethodPtrCall ptrcall_func;
324                 flags, //uint32_t method_flags; /* GDNativeExtensionClassMethodFlags */
325                 cast(uint32_t) arity!mf, //uint32_t argument_count;
326                 cast(GDNativeBool) !is(ReturnType!mf == void), //GDNativeBool has_return_value;
327                 &MethodWrapperMeta!mf.getArgTypesFn, //(GDNativeExtensionClassMethodGetArgumentType) get_argument_type_func;
328                 &MethodWrapperMeta!mf.getArgInfoFn, //GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func; /* name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. */
329                 &MethodWrapperMeta!mf.getArgMetadataFn, //GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func;
330                 MethodWrapperMeta!mf.getDefaultArgNum, //uint32_t default_argument_count;
331                 MethodWrapperMeta!mf.getDefaultArgs(), //GDNativeVariantPtr *default_arguments;
332         
333         };
334 
335         _godot_api.classdb_register_extension_class_method(lib, name, &mi);
336     }
337 
338     void registerMemberAccessor(alias mf, alias propType, string funcName)() {
339         static assert(Parameters!propType.length == 0 || Parameters!propType.length == 1,
340             "only getter or setter is allowed with exactly zero or one arguments");
341 
342         static if (funcName) {
343             char[funcName.length + 1] mfn = void;
344             mfn[0 .. funcName.length] = funcName;
345             mfn[$ - 1] = '\0';
346         } else
347             char[1] mfn = '\0';
348 
349         uint flags = GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT;
350 
351         GDNativeExtensionClassMethodInfo mi = {
352             mfn.ptr, //const char *name;
353                 &mf, //void *method_userdata;
354                 &mf, //GDNativeExtensionClassMethodCall call_func;
355                 null, //GDNativeExtensionClassMethodPtrCall ptrcall_func;
356                 flags, //uint32_t method_flags; /* GDNativeExtensionClassMethodFlags */
357                 cast(uint32_t) arity!propType, //uint32_t argument_count;
358                 cast(GDNativeBool) !is(ReturnType!mf == void), //GDNativeBool has_return_value;
359                 &MethodWrapperMeta!propType.getArgTypesFn, //(GDNativeExtensionClassMethodGetArgumentType) get_argument_type_func;
360                 &MethodWrapperMeta!propType.getArgInfoFn, //GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func; /* name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. */
361                 &MethodWrapperMeta!propType.getArgMetadataFn, //GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func;
362                 MethodWrapperMeta!propType.getDefaultArgNum, //uint32_t default_argument_count;
363                 MethodWrapperMeta!propType.getDefaultArgs(), //GDNativeVariantPtr *default_arguments;
364         
365         };
366 
367         _godot_api.classdb_register_extension_class_method(lib, name.ptr, &mi);
368     }
369 
370     static foreach (mf; godotMethods!T) {
371         {
372             static if (hasUDA!(mf, Rename))
373                 enum string externalName = godotName!mf;
374             else
375                 enum string externalName = (fullyQualifiedName!mf).replace(".", "_");
376             registerMethod!mf();
377         }
378     }
379 
380     static foreach (sName; godotSignals!T) {
381         {
382             alias s = Alias!(mixin("T." ~ sName));
383             static assert(hasStaticMember!(T, sName), "Signal declaration " ~ fullyQualifiedName!s
384                     ~ " must be static. Otherwise it would take up memory in every instance of " ~ T
385                     .stringof);
386 
387             static if (hasUDA!(s, Rename))
388                 enum string externalName = godotName!s;
389             else
390                 enum string externalName = (fullyQualifiedName!s).replace(".", "_");
391 
392             __gshared static GDNativePropertyInfo[Parameters!s.length] prop;
393             static foreach (int i, p; Parameters!s) {
394                 static assert(Variant.compatible!p, fullyQualifiedName!s ~ " parameter " ~ i.text ~ " \""
395                         ~ ParameterIdentifierTuple!s[i] ~ "\": type " ~ p.stringof ~ " is incompatible with Godot");
396 
397                 // get name or argN fallback placeholder in case of function pointers
398                 static if (ParameterIdentifierTuple!s[i].length > 0)
399                     prop[i].name = ParameterIdentifierTuple!s[i];
400                 else
401                     prop[i].name = "arg" ~ i.stringof;
402 
403                 if (Variant.variantTypeOf!p == VariantType.object)
404                     prop[i].class_name = name.ptr;
405                 prop[i].type = Variant.variantTypeOf!p;
406                 prop[i].hint = 0;
407                 prop[i].hint_string = null;
408                 prop[i].usage = GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT;
409             }
410 
411             _godot_api.classdb_register_extension_class_signal(lib, name.ptr, (externalName).ptr, prop.ptr, Parameters!s
412                     .length);
413         }
414     }
415 
416     // -------- PROPERTIES
417 
418     enum bool matchName(string p, alias a) = (godotName!a == p);
419     static foreach (pName; godotPropertyNames!T) {
420         {
421             enum propName = pName ~ '\0';
422 
423             alias getterMatches = Filter!(ApplyLeft!(matchName, pName), godotPropertyGetters!T);
424             static assert(getterMatches.length <= 1); /// TODO: error message
425             alias setterMatches = Filter!(ApplyLeft!(matchName, pName), godotPropertySetters!T);
426             static assert(setterMatches.length <= 1);
427 
428             static if (getterMatches.length)
429                 alias P = NonRef!(ReturnType!(getterMatches[0]));
430             else
431                 alias P = Parameters!(setterMatches[0])[0];
432             //static assert(!is(P : Ref!U, U)); /// TODO: proper Ref handling
433             enum VariantType vt = extractPropertyVariantType!(getterMatches, setterMatches);
434 
435             enum Property uda = extractPropertyUDA!(getterMatches, setterMatches);
436 
437             __gshared static GDNativePropertyInfo pinfo;
438 
439             static if (Variant.variantTypeOf!P == VariantType.object)
440                 pinfo.class_name = godotName!(P).ptr;
441             pinfo.type = vt;
442             pinfo.name = propName.ptr;
443             pinfo.hint = GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_NONE;
444             //pinfo.usage = GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT | GDNATIVE_EXTENSION_METHOD_FLAG_EDITOR;
445             pinfo.usage = 7; // godot-cpp uses 7 as value which is default|const|editor currently, doesn't shows up in inspector without const. WTF?
446             static if (uda.hintString.length)
447                 pinfo.hint_string = uda.hintString;
448 
449             // register acessor methods for that property
450             static if (getterMatches.length) {
451                 enum get_prop = "get_" ~ pName ~ '\0';
452                 registerMethod!(getterMatches[0], get_prop);
453             } else
454                 enum get_prop = string.init;
455 
456             static if (setterMatches.length) {
457                 enum set_prop = "set_" ~ pName ~ '\0';
458                 registerMethod!(setterMatches[0], set_prop);
459             } else
460                 enum set_prop = string.init;
461 
462             _godot_api.classdb_register_extension_class_property(lib, name.ptr, &pinfo, set_prop.ptr, get_prop
463                     .ptr);
464         }
465     }
466     static foreach (pName; godotPropertyVariableNames!T) {
467         {
468             import std.string;
469 
470             enum propName = pName ~ '\0';
471             alias P = typeof(mixin("T." ~ pName));
472             enum Variant.Type vt = Variant.variantTypeOf!P;
473             alias udas = getUDAs!(mixin("T." ~ pName), Property);
474             enum Property uda = is(udas[0]) ? Property.init : udas[0];
475 
476             __gshared static GDNativePropertyInfo pinfo;
477 
478             static if (Variant.variantTypeOf!P == VariantType.object)
479                 pinfo.class_name = godotName!(P).ptr,
480                 pinfo.type = vt;
481             pinfo.name = propName.ptr;
482             pinfo.usage = GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT | GDNATIVE_EXTENSION_METHOD_FLAG_EDITOR;
483             static if (uda.hintString.length)
484                 pinfo.hint_string = uda.hintString;
485 
486             // register acessor methods for that property
487             enum get_prop = "get_" ~ pName ~ '\0';
488             alias fnWrapper = VariableWrapper!(T, __traits(getMember, T, pName));
489             static fnWrapper.getterType getterTmp; // dummy func for now because current registration code requires actual function, and there isn't one
490             registerMemberAccessor!(fnWrapper.callPropertyGet, getterTmp, get_prop);
491 
492             enum set_prop = "set_" ~ pName ~ '\0';
493             static fnWrapper.setterType setterTmp; // dummy func for now because current registration code requires actual function, and there isn't one
494             registerMemberAccessor!(fnWrapper.callPropertySet, setterTmp, set_prop);
495 
496             _godot_api.classdb_register_extension_class_property(lib, name.ptr, &pinfo, set_prop.ptr, get_prop
497                     .ptr);
498         }
499     }
500 
501     static foreach (E; godotEnums!T) {
502         {
503             static foreach (ev; __traits(allMembers, E)) {
504                 //pragma(msg, ev, ":", cast(int) __traits(getMember, E, ev));
505                 _godot_api.classdb_register_extension_class_integer_constant(lib, name.ptr,
506                     __traits(identifier, E), ev, cast(int) __traits(getMember, E, ev), false);
507             }
508         }
509     }
510 
511     static foreach (pName; godotConstants!T) {
512         {
513             alias E = __traits(getMember, T, pName);
514             //pragma(msg, pName, ":", cast(int) E);
515             _godot_api.classdb_register_extension_class_integer_constant(lib, name.ptr, null, pName, cast(
516                     int) E, false);
517         }
518     }
519 
520     /*
521 	auto icf = godot_instance_create_func(&createFunc!T, null, null);
522 	auto idf = godot_instance_destroy_func(&destroyFunc!T, null, null);
523 	
524 	static if(hasUDA!(T, Tool)) _godot_nativescript_api.godot_nativescript_register_tool_class(handle, name, baseName, icf, idf);
525 	else _godot_nativescript_api.godot_nativescript_register_class(handle, name, baseName, icf, idf);
526 	
527 	if(GDNativeVersion.hasNativescript!(1, 1))
528 	{
529 		_godot_nativescript_api.godot_nativescript_set_type_tag(handle, name, NativeScriptTag!T.tag);
530 	}
531 	else // register a no-op function that indicates this is a D class
532 	{
533 		godot_instance_method md;
534 		md.method = &_GODOT_nop;
535 		md.free_func = null;
536 		_godot_nativescript_api.godot_nativescript_register_method(handle, name, "_GDNATIVE_D_typeid", godot_method_attributes.init, md);
537 	}
538 	
539 	static foreach(mf; godotMethods!T)
540 	{{
541 		godot_method_attributes ma;
542 		static if(is( getUDAs!(mf, Method)[0] )) ma.rpc_type = godot_method_rpc_mode
543 			.GODOT_METHOD_RPC_MODE_DISABLED;
544 		else
545 		{
546 			ma.rpc_type = cast(godot_method_rpc_mode)(getUDAs!(mf, Method)[0].rpcMode);
547 		}
548 		
549 		godot_instance_method md;
550 		static if(godotName!mf == "_ready" && onReadyFieldNames!T.length)
551 		{
552 			md.method = &OnReadyWrapper!T.callOnReady;
553 		}
554 		else md.method = &MethodWrapper!(T, mf).callMethod;
555 		md.free_func = null;
556 		
557 		char[godotName!mf.length+1] mfn = void;
558 		mfn[0..godotName!mf.length] = godotName!mf[];
559 		mfn[$-1] = '\0';
560 		_godot_nativescript_api.godot_nativescript_register_method(handle, name, mfn.ptr, ma, md);
561 	}}
562 	
563 	// OnReady when there is no _ready method
564 	static if(staticIndexOf!("_ready", staticMap!(godotName, godotMethods!T)) == -1
565 		&& onReadyFieldNames!T.length)
566 	{
567 		enum ma = godot_method_attributes.init;
568 		godot_instance_method md;
569 		md.method = &OnReadyWrapper!T.callOnReady;
570 		_godot_nativescript_api.godot_nativescript_register_method(handle, name, "_ready", ma, md);
571 	}
572 	
573 	static foreach(sName; godotSignals!T)
574 	{{
575 		alias s = Alias!(mixin("T."~sName));
576 		static assert(hasStaticMember!(T, sName), "Signal declaration "~fullyQualifiedName!s
577 			~" must be static. Otherwise it would take up memory in every instance of "~T.stringof);
578 		
579 		godot_signal gs;
580 		(*cast(String*)&gs.name) = String(godotName!s);
581 		gs.num_args = Parameters!s.length;
582 		
583 		static if(Parameters!s.length)
584 		{
585 			godot_signal_argument[Parameters!s.length] args;
586 			gs.args = args.ptr;
587 		}
588 		
589 		foreach(pi, P; Parameters!s)
590 		{
591 			static assert(Variant.compatible!P, fullyQualifiedName!s~" parameter "~pi.text~" \""
592 				~ParameterIdentifierTuple!s[pi]~"\": type "~P.stringof~" is incompatible with Godot");
593 			static if(ParameterIdentifierTuple!s[pi].length > 0)
594 			{
595 				(*cast(String*)&args[pi].name) = String(ParameterIdentifierTuple!s[pi]);
596 			}
597 			else
598 			{
599 				(*cast(String*)&args[pi].name) = (String(P.stringof) ~ String("Arg") ~ Variant(pi).as!String);
600 			}
601 			args[pi].type = Variant.variantTypeOf!P;
602 			args[pi].usage = cast(godot_property_usage_flags)Property.Usage.defaultUsage;
603 		}
604 		
605 		_godot_nativescript_api.godot_nativescript_register_signal(handle, name, &gs);
606 	}}
607 	
608 	enum bool matchName(string p, alias a) = (godotName!a == p);
609 	static foreach(pName; godotPropertyNames!T)
610 	{{
611 		alias getterMatches = Filter!(ApplyLeft!(matchName, pName), godotPropertyGetters!T);
612 		static assert(getterMatches.length <= 1); /// TODO: error message
613 		alias setterMatches = Filter!(ApplyLeft!(matchName, pName), godotPropertySetters!T);
614 		static assert(setterMatches.length <= 1);
615 		
616 		godot_property_set_func sf;
617 		godot_property_get_func gf;
618 		godot_property_attributes attr;
619 		
620 		static if(getterMatches.length) alias P = NonRef!(ReturnType!(getterMatches[0]));
621 		else alias P = Parameters!(setterMatches[0])[0];
622 		static assert(!is(P : Ref!U, U)); /// TODO: proper Ref handling
623 		enum Variant.Type vt = extractPropertyVariantType!(getterMatches, setterMatches);
624 		attr.type = cast(godot_int)vt;
625 		
626 		enum Property uda = extractPropertyUDA!(getterMatches, setterMatches);
627 		attr.rset_type = cast(godot_method_rpc_mode)uda.rpcMode;
628 		attr.hint = cast(godot_property_hint)uda.hint;
629 
630 		static if(vt == Variant.Type.object && extends!(P, Resource))
631 		{
632 			attr.hint |= godot_property_hint.GODOT_PROPERTY_HINT_RESOURCE_TYPE;
633 		}
634 
635 		static if(uda.hintString.length) _godot_api.string_parse_utf8(
636 			&attr.hint_string, uda.hintString.ptr);
637 		else
638 		{
639 			static if(vt == Variant.Type.object)
640 			{
641 				_godot_api.string_parse_utf8(&attr.hint_string,
642 					GodotClass!P._GODOT_internal_name);
643 			}
644 			else _godot_api.string_new(&attr.hint_string);
645 		}
646 		attr.usage = cast(godot_property_usage_flags)(uda.usage |
647 			Property.Usage.scriptVariable);
648 		
649 		Variant defval;
650 		static if(getterMatches.length) enum gDef = hasUDA!(getterMatches[0], DefaultValue);
651 		else enum gDef = false;
652 		static if(setterMatches.length) enum sDef = hasUDA!(setterMatches[0], DefaultValue);
653 		else enum sDef = false;
654 
655 		static if(gDef || sDef)
656 		{
657 			static if(gDef) alias defExprSeq = TemplateArgsOf!(getUDAs!(getterMatches[0], DefaultValue)[0]);
658 			else alias defExprSeq = TemplateArgsOf!(getUDAs!(setterMatches[0], DefaultValue)[0]);
659 			defval = defExprSeq[0];
660 		}
661 		else static if( is(typeof( { P p; } )) ) // use type's default value
662 		{
663 			static if(isFloatingPoint!P)
664 			{
665 				// Godot doesn't support NaNs. Initialize properties to 0.0 instead.
666 				defval = 0.0;
667 			}
668 			else defval = P.init;
669 		}
670 		else
671 		{
672 			/// FIXME: call default constructor function
673 			defval = null;
674 		}
675 		attr.default_value = defval._godot_variant;
676 		
677 		static if(getterMatches.length)
678 		{
679 			alias GetWrapper = MethodWrapper!(T, getterMatches[0]);
680 			gf.get_func = &GetWrapper.callPropertyGet;
681 			gf.free_func = null;
682 		}
683 		else
684 		{
685 			gf.get_func = &emptyGetter;
686 		}
687 		
688 		static if(setterMatches.length)
689 		{
690 			alias SetWrapper = MethodWrapper!(T, setterMatches[0]);
691 			sf.set_func = &SetWrapper.callPropertySet;
692 			sf.free_func = null;
693 		}
694 		else
695 		{
696 			sf.set_func = &emptySetter;
697 		}
698 		
699 		char[pName.length+1] pn = void;
700 		pn[0..pName.length] = pName[];
701 		pn[$-1] = '\0';
702 		_godot_nativescript_api.godot_nativescript_register_property(handle, name, pn.ptr, &attr, sf, gf);
703 	}}
704 	static foreach(pName; godotPropertyVariableNames!T)
705 	{{
706 		import std.string;
707 		
708 		godot_property_set_func sf;
709 		godot_property_get_func gf;
710 		godot_property_attributes attr;
711 		
712 		alias P = typeof(mixin("T."~pName));
713 		enum Variant.Type vt = Variant.variantTypeOf!P;
714 		attr.type = cast(godot_int)vt;
715 		
716 		alias udas = getUDAs!(mixin("T."~pName), Property);
717 		enum Property uda = is(udas[0]) ? Property.init : udas[0];
718 		attr.rset_type = cast(godot_method_rpc_mode)uda.rpcMode;
719 		attr.hint = cast(godot_property_hint)uda.hint;
720 
721 		static if(vt == Variant.Type.object && is(GodotClass!P : Resource))
722 		{
723 			attr.hint |= godot_property_hint.GODOT_PROPERTY_HINT_RESOURCE_TYPE;
724 		}
725 
726 		static if(uda.hintString.length) _godot_api.string_parse_utf8(
727 			&attr.hint_string, uda.hintString.ptr);
728 		else
729 		{
730 			static if(vt == Variant.Type.object)
731 			{
732 				_godot_api.string_parse_utf8(&attr.hint_string,
733 					GodotClass!P._GODOT_internal_name);
734 			}
735 			else _godot_api.string_new(&attr.hint_string);
736 		}
737 		attr.usage = cast(godot_property_usage_flags)uda.usage |
738 			cast(godot_property_usage_flags)Property.Usage.scriptVariable;
739 		
740 		Variant defval = getDefaultValueFromAlias!(T, pName)();
741 		attr.default_value = defval._godot_variant;
742 		
743 		alias Wrapper = VariableWrapper!(T, pName);
744 		
745 		{
746 			gf.method_data = null;
747 			gf.get_func = &Wrapper.callPropertyGet;
748 			gf.free_func = null;
749 		}
750 		
751 		{
752 			sf.method_data = null;
753 			sf.set_func = &Wrapper.callPropertySet;
754 			sf.free_func = null;
755 		}
756 		
757 		enum pnLength = godotName!(mixin("T."~pName)).length;
758 		char[pnLength+1] pn = void;
759 		pn[0..pnLength] = godotName!(mixin("T."~pName))[];
760 		pn[$-1] = '\0';
761 		_godot_nativescript_api.godot_nativescript_register_property(handle, name, pn.ptr, &attr, sf, gf);
762 	}}
763 	
764 	
765 	
766 	godot.api.script.NativeScriptTemplate!T = memnew!(godot.nativescript.NativeScript);
767 	godot.api.script.NativeScriptTemplate!T.setLibrary(lib);
768 	godot.api.script.NativeScriptTemplate!T.setClassName(String(name));
769 	*/
770 }