// Copyright © 2019 - 2020 Oscar Campos // Copyright © 2017 - William Edwards // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License // Package gdnative provides a wrapper around the Godot GDNative API. package gdnative /* #cgo CFLAGS: -I../godot_headers -std=c11 #cgo linux LDFLAGS: -Wl,-unresolved-symbols=ignore-all #cgo darwin LDFLAGS: -Wl,-undefined,dynamic_lookup #include #include #include "gdnative.gen.h" #include "util.h" */ import "C" import ( "fmt" "log" "unsafe" wchar "github.com/vitaminwater/cgo.wchar" ) // debug determines whether or not we should log messages var debug = false // EnableDebug will enable logging of GDNative debug messages. func EnableDebug() { debug = true } // GDNative is our API main entry point, it is first set here to null. // This will be set when 'godot_gdnative_init' is called by Godot when the library is loaded. var GDNative = new(gdNative) // gdNative is a structure that wraps the GDNativeAPI. type gdNative struct { api *C.godot_gdnative_core_api_struct initialized bool } // IsInitialized will return true if `godot_gdnative_init` was called by Godot. func (g *gdNative) IsInitialized() bool { return g.initialized } // CheckInit will check to see if GDNative has initialized. If it is not, it will // throw a panic. func (g *gdNative) checkInit() { if !g.IsInitialized() { panic("GDNative has not initialized! You cannot call this in init()!") } } // godot_gdnative_init is the library entry point. When the library is loaded // this method will be called by Godot. //export godot_gdnative_init func godot_gdnative_init(options *C.godot_gdnative_init_options) { // Get the API struct from the init options passed by Godot when this // library is loaded. This API struct will have all of the functions // to call. GDNative.api = (*options).api_struct GDNative.initialized = true // Configure logging. log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetOutput(Log) if debug { log.Println("Initializing gdnative-go library.") } // Find GDNative extensions that we support. for i := 0; i < int(GDNative.api.num_extensions); i++ { extension := C.cgo_get_ext(GDNative.api.extensions, C.int(i)) switch extension._type { case C.GDNATIVE_EXT_NATIVESCRIPT: if debug { log.Println("Found nativescript extension!") } NativeScript.api = (*C.godot_gdnative_ext_nativescript_api_struct)(unsafe.Pointer(extension)) } } } /** Library de-initialization **/ // godot_gdnative_terminate is the library's de-initialization method. When // Godot unloads the library, this method will be called. //export godot_gdnative_terminate func godot_gdnative_terminate(options *C.godot_gdnative_terminate_options) { if debug { log.Println("De-initializing Go library.") } GDNative.api = nil NativeScript.api = nil } // NewEmptyVoid returns back a new C empty or void pointer func NewEmptyVoid() Pointer { var empty C.void return Pointer{base: unsafe.Pointer(&empty)} } // GetSingleton will return an instance of the given singleton. func GetSingleton(name String) Object { if debug { log.Println("Getting singleton:", name) } GDNative.checkInit() // Create a C string from the name argument. cName := C.CString(string(name)) // Call the C method obj := C.go_godot_global_get_singleton(GDNative.api, cName) return Object{base: (*C.godot_object)(obj)} } // NewMethodBind will return a method binding using the given class name and method // name. func NewMethodBind(class, method string) MethodBind { if debug { log.Println("Creating method bind for:", class+"."+method) } GDNative.checkInit() methodBind := C.go_godot_method_bind_get_method( GDNative.api, C.CString(class), C.CString(method), ) return MethodBind{base: methodBind} } // MethodBindPtrCall will call the given method on the given Godot Object. Its return // value is given as a pointer, which can be used to convert it to a variant. func MethodBindPtrCall(methodBind MethodBind, instance Object, args []Pointer, returns Pointer) Pointer { GDNative.checkInit() if instance.getBase() == nil { panic("Godot object pointer was nil when calling MethodBindPtrCall") } // Build out our C arguments array cArgs := C.go_void_build_array(C.int(len(args))) for i, arg := range args { C.go_void_add_element(cArgs, arg.getBase(), C.int(i)) } if debug { // If debug is enabled print all the arguments we are passing for i, arg := range args { log.Println("arg", i, ": ", arg.getBase()) } log.Println("args: ", cArgs) log.Println("returns: ", returns.getBase()) log.Println("object: ", unsafe.Pointer(instance.getBase())) log.Println("methodbind: ", unsafe.Pointer(methodBind.getBase())) } // Call the C method C.go_godot_method_bind_ptrcall( GDNative.api, methodBind.getBase(), unsafe.Pointer(instance.getBase()), cArgs, returns.getBase(), ) if debug { log.Println("Finished calling method.") } return returns } // Pointer is a pointer to arbitrary underlying data. This is primarily used // in conjunction with MethodBindPtrCall. type Pointer struct { base unsafe.Pointer } func (p *Pointer) getBase() unsafe.Pointer { return p.base } // Char is a Godot C char wrapper type Char string func (c Char) getBase() *C.char { // this might lead to memory leaks as this C memory // is not gonna be garbage collected by Go runtime return C.CString(string(c)) } // Double is a Godot C double wrapper type Double float64 func (d Double) getBase() C.double { return C.double(d) } // NewPointerFromFloat will return an unsafe pointer to the given // object. This is primarily used in conjunction with MethodBindPtrCall. func NewPointerFromFloat(obj Float) Pointer { base := obj.getBase() return Pointer{base: unsafe.Pointer(&base)} } // NewFloatFromPointer will return a Float from the // given unsafe pointer. This is primarily used in conjunction with MethodBindPtrCall. func NewFloatFromPointer(ptr Pointer) Float { base := ptr.getBase() return Float(*(*C.float)(base)) } // NewEmptyFloat will return a pointer to an empty // initialized Float. This is primarily used in // conjunction with MethodBindPtrCall. func NewEmptyFloat() Pointer { var obj C.float return Pointer{base: unsafe.Pointer(&obj)} } // Float is a Godot C float wrapper type Float float64 func (f Float) getBase() C.float { return C.float(f) } // Int64T is a Godot C int64_t wrapper type Int64T int64 func (i Int64T) getBase() C.int64_t { return C.int64_t(i) } // SignedChar is a Godot C schar wrapper type SignedChar int8 func (s SignedChar) getBase() *C.schar { //nolint:unused intVal := int8(s) return (*C.schar)(unsafe.Pointer(&intVal)) } // Uint is a Godot C uint wrapper type Uint uint func (u Uint) getBase() C.uint { //nolint:unused return C.uint(u) } // Uint8T is a Godot C uint8_t wrapper type Uint8T uint8 func (u Uint8T) getBase() C.uint8_t { return C.uint8_t(u) } // Uint32T is a Godot C uint32_t wrapper type Uint32T uint32 func (u Uint32T) getBase() C.uint32_t { //nolint:unused return C.uint32_t(u) } // Uint64T is a Godot C uint64_t wrapper type Uint64T uint64 func (u Uint64T) getBase() C.uint64_t { return C.uint64_t(u) } // newWcharT will convert the given C.wchar_t into a Go string func newWcharT(str *C.wchar_t) WcharT { goStr, err := wchar.WcharStringPtrToGoString(unsafe.Pointer(str)) if err != nil { log.Println("Error converting wchar_t to Go string:", err) } return WcharT(goStr) } // WcharT is a Godot C wchar_t wrapper type WcharT string func (w WcharT) getBase() *C.wchar_t { wcharString, err := wchar.FromGoString(string(w)) if err != nil { log.Println("Error decoding WcharT:", err) } return (*C.wchar_t)(wcharString.Pointer()) } // AsString converts a WCharT into a string func (w WcharT) AsString() String { return NewStringWithWideString(string(w)) } // ID will return the Godot object memory address as a string, which can // be used in an instance registry for registering classes. func (gdt Object) ID() string { return fmt.Sprintf("%p", gdt.base) }