// 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 Godot's nativescript // extension. It exists to provide a way to use Go as an alternative scripting // language from GDScript. package gdnative /* #include #include "nativescript.gen.h" #include "nativescript.h" #include "variant.h" */ import "C" import ( "log" "unsafe" ) // PropertyUsageFlags is a Godot C godot_property_usage_flags wrapper type PropertyUsageFlags int func (p PropertyUsageFlags) getBase() C.godot_property_usage_flags { return C.godot_property_usage_flags(p) } // Property usage flags for bit masks const ( PropertyUsageStorage PropertyUsageFlags = 1 PropertyUsageEditor PropertyUsageFlags = 2 PropertyUsageNetwork PropertyUsageFlags = 4 PropertyUsageEditorHelper PropertyUsageFlags = 8 PropertyUsageCheckable PropertyUsageFlags = 16 //used for editing global variables PropertyUsageChecked PropertyUsageFlags = 32 //used for editing global variables PropertyUsageInternationalized PropertyUsageFlags = 64 //hint for internationalized strings PropertyUsageGroup PropertyUsageFlags = 128 //used for grouping props in the editor PropertyUsageCategory PropertyUsageFlags = 256 PropertyUsageStoreIfNonZero PropertyUsageFlags = 512 //only store if nonzero PropertyUsageStoreIfNonOne PropertyUsageFlags = 1024 //only store if false PropertyUsageNoInstanceState PropertyUsageFlags = 2048 PropertyUsageRestartIfChanged PropertyUsageFlags = 4096 PropertyUsageScriptVariable PropertyUsageFlags = 8192 PropertyUsageStoreIfNull PropertyUsageFlags = 16384 PropertyUsageAnimateAsTrigger PropertyUsageFlags = 32768 PropertyUsageUpdateAllIfModified PropertyUsageFlags = 65536 PropertyUsageDefault PropertyUsageFlags = PropertyUsageStorage | PropertyUsageEditor | PropertyUsageNetwork PropertyUsageDefaultIntl PropertyUsageFlags = PropertyUsageStorage | PropertyUsageEditor | PropertyUsageNetwork | PropertyUsageInternationalized PropertyUsageNoEditor PropertyUsageFlags = PropertyUsageStorage | PropertyUsageNetwork ) // PropertyUsageFlagsLookupMap is a string-based lookup table of constants for PropertyUsageFlags. var PropertyUsageFlagsLookupMap = map[string]PropertyUsageFlags{ "PropertyUsageStorage": PropertyUsageStorage, "PropertyUsageEditor": PropertyUsageEditor, "PropertyUsageNetwork": PropertyUsageNetwork, "PropertyUsageEditorHelper": PropertyUsageEditorHelper, "PropertyUsageCheckable": PropertyUsageCheckable, "PropertyUsageChecked": PropertyUsageChecked, "PropertyUsageInternationalized": PropertyUsageInternationalized, "PropertyUsageGroup": PropertyUsageGroup, "PropertyUsageCategory": PropertyUsageCategory, "PropertyUsageStoreIfNonZero": PropertyUsageStoreIfNonZero, "PropertyUsageStoreIfNonOne": PropertyUsageStoreIfNonOne, "PropertyUsageNoInstanceState": PropertyUsageNoInstanceState, "PropertyUsageRestartIfChanged": PropertyUsageRestartIfChanged, "PropertyUsageScriptVariable": PropertyUsageScriptVariable, "PropertyUsageStoreIfNull": PropertyUsageStoreIfNull, "PropertyUsageAnimateAsTrigger": PropertyUsageAnimateAsTrigger, "PropertyUsageUpdateAllIfModified": PropertyUsageUpdateAllIfModified, "PropertyUsageDefault": PropertyUsageDefault, "PropertyUsageDefaultIntl": PropertyUsageDefaultIntl, "PropertyUsageNoEditor": PropertyUsageNoEditor, } // CreateFunc will be called when we need to create a new instance of a class. // When it is called, the Godot object will passed as an argument, as well as the // methodData string, which is usually the name of the class to be created. type CreateFunc func(Object, string) string // CreateFuncRegistry is a mapping of instance creation functions. This map is // used whenever a CreateFunc is registered. It is also used to look up a // Creation function when Godot asks Go to create a new class instance. var CreateFuncRegistry = map[string]CreateFunc{} // DestroyFunc will be called when the object is destroyed. Takes the instance // object, method data, user data. The method data is generally the class name, // and the user data is generally the class instance id to destroy. type DestroyFunc func(Object, string, string) // DestroyFuncRegistry is a mapping of instance destroy functions. This map is // used whenever a DestroyFunc is registered. It is also used to look up a // Destroy function when Godot asks Go to destroy a class instance. var DestroyFuncRegistry = map[string]DestroyFunc{} // FreeFunc will be called when we should free the instance from memory. Takes // in method data. The method data is generally the class name to be freed. type FreeFunc func(string) // SetPropertyFunc will be called when Godot requests to set a property on a given // class. When it is called, the Godot object instance will be passed as an argument, // as well as the methodData (which is usually the name of the class), the // userData (which is generally the instance ID of the object), and the value // to set. type SetPropertyFunc func(Object, string, string, Variant) // SetPropertyFuncRegistry is a mapping of instance property setters. This map is // used whenever a SetPropertyFunc is registered. It is also used to look up // a property setter function when Godot asks Go to set a property on an object. var SetPropertyFuncRegistry = map[string]SetPropertyFunc{} // GetPropertyFunc will be called when Godot requests a property on a given class // instance. When it is called, Godot will pass the Godot object instance as an // argument, as well as the methodData (which is usually the name of the class), // and the userData (which is generally the instance ID of the object. You should // return the property as a Variant. type GetPropertyFunc func(Object, string, string) Variant // GetPropertyFuncRegistry is a mapping of instance property getters. This map is // used whenever a GetPropertyFunc is registered. It is also used to look up a // property getter function when Godot asks Go to get a property on an object. var GetPropertyFuncRegistry = map[string]GetPropertyFunc{} // FreeFuncRegistry is a mapping of instance free functions. This map is used // whenever a FreeFunc is registered. It is also used to look up a Free // function when Godot asks Go to free a class instance. var FreeFuncRegistry = map[string]FreeFunc{} // MethodFunc will be called when a method attached to an instance is called. // When it is called, it will be passed the Godot object the method is attached to, // the methodData string (which is usually the class and method name that is being called), // the userData string (which is usually the class instance ID), the number of // arguments being passed to the function, and a list of Variant arguments to pass // to the function. type MethodFunc func(Object, string, string, int, []Variant) Variant // MethodFuncRegistry is a mapping of instance method functions. This map is // used whenever a MethodFunc is registered. It is also used to look up a Method // function when Godot asks Go to call a class method. var MethodFuncRegistry = map[string]MethodFunc{} // InstanceCreateFunc is a structure that contains the instance creation function // that will be called when Godot asks Go to create a new instance of a class. type InstanceCreateFunc struct { base C.godot_instance_create_func CreateFunc CreateFunc MethodData string FreeFunc FreeFunc } func (i *InstanceCreateFunc) getBase() C.godot_instance_create_func { return i.base } // InstanceDestroyFunc is a structure that contains the instance destruction function // that will be called when Godot asks Go to destroy a class instance. type InstanceDestroyFunc struct { base C.godot_instance_destroy_func DestroyFunc DestroyFunc MethodData string FreeFunc FreeFunc } func (i *InstanceDestroyFunc) getBase() C.godot_instance_destroy_func { return i.base } // InstanceMethod is a structure that contains an instance method function that // will be called when Godot asks Go to call a method on a class. type InstanceMethod struct { base C.godot_instance_method Method MethodFunc MethodData string FreeFunc FreeFunc } func (i *InstanceMethod) getBase() C.godot_instance_method { return i.base } // MethodAttributes is a Godot C godot_method_attributes wrapper type MethodAttributes struct { base C.godot_method_attributes RPCType MethodRpcMode } func (m *MethodAttributes) getBase() C.godot_method_attributes { return m.base } // InstancePropertySet is a structure that contains an instance property setter // function that will be called when Godot asks Go to set a property on a class. type InstancePropertySet struct { base C.godot_property_set_func SetFunc SetPropertyFunc MethodData string FreeFunc FreeFunc } func (i *InstancePropertySet) getBase() C.godot_property_set_func { return i.base } // InstancePropertyGet is a structure that contains an instance property getter // function that will be called when Godot asks Go to get a property from a class // instance. type InstancePropertyGet struct { base C.godot_property_get_func GetFunc GetPropertyFunc MethodData string FreeFunc FreeFunc } func (i *InstancePropertyGet) getBase() C.godot_property_get_func { return i.base } // NativeScript is a wrapper for the NativeScriptAPI. var NativeScript = new(nativeScript) // nativeScript is a structure that wraps the NativeScriptAPI methods. type nativeScript struct { api *C.godot_gdnative_ext_nativescript_api_struct // Handle is a pointer to the gdnative handler. It must be passed to any // Godot nativescript functions. This will be populated when 'godot_nativescript_init' // is called by Godot upon script initialization. handle unsafe.Pointer } // RegisterClass will register the given class with Godot. This will make it // available to be attached to a Node in Godot. The name of the class that you // provide here will be the name that you specify when you attach a NativeScript // script to a Node. The base parameter that you specify will be what the class // should be inheriting from (e.g. Node2D, Node, etc.). // // The create and destroy function parameters are C structs that contain // function pointers to C methods that will be called when the object is created // or destroyed. Because of the pointer passing rules, Go code can not pass a // function value directly to C, so a gateway function should be used. More // information on using a gateway function can be found here: // https://github.com/golang/go/wiki/cgo#function-variables func (n *nativeScript) RegisterClass(name, base string, createFunc *InstanceCreateFunc, destroyFunc *InstanceDestroyFunc) { // Construct the C struct based on the Go struct wrappers createFunc.base.create_func = (C.create_func)(unsafe.Pointer(C.cgo_gateway_create_func)) createFunc.base.method_data = unsafe.Pointer(C.CString(createFunc.MethodData)) createFunc.base.free_func = (C.free_func)(unsafe.Pointer(C.cgo_gateway_free_func)) destroyFunc.base.destroy_func = (C.destroy_func)(unsafe.Pointer(C.cgo_gateway_destroy_func)) destroyFunc.base.method_data = unsafe.Pointer(C.CString(destroyFunc.MethodData)) destroyFunc.base.free_func = (C.free_func)(unsafe.Pointer(C.cgo_gateway_free_func)) // Register our Create and Destroy functions in a Go map, so the correct // function can be called when cgo_gateway__func is called. CreateFuncRegistry[createFunc.MethodData] = createFunc.CreateFunc FreeFuncRegistry[createFunc.MethodData] = createFunc.FreeFunc DestroyFuncRegistry[destroyFunc.MethodData] = destroyFunc.DestroyFunc FreeFuncRegistry[destroyFunc.MethodData] = destroyFunc.FreeFunc // Register the class with Godot. C.go_godot_nativescript_register_class( n.api, n.handle, C.CString(name), C.CString(base), createFunc.getBase(), destroyFunc.getBase(), ) } // RegisterToolClass will register the given class with Godot as a tool. Refer to // the 'RegisterClass' method for more information on how to use this. func (n *nativeScript) RegisterToolClass(name, base string, createFunc *InstanceCreateFunc, destroyFunc *InstanceDestroyFunc) { // Construct the C struct based on the Go struct wrappers createFunc.base.create_func = (C.create_func)(unsafe.Pointer(C.cgo_gateway_create_func)) createFunc.base.method_data = unsafe.Pointer(C.CString(createFunc.MethodData)) createFunc.base.free_func = (C.free_func)(unsafe.Pointer(C.cgo_gateway_free_func)) destroyFunc.base.destroy_func = (C.destroy_func)(unsafe.Pointer(C.cgo_gateway_destroy_func)) destroyFunc.base.method_data = unsafe.Pointer(C.CString(destroyFunc.MethodData)) destroyFunc.base.free_func = (C.free_func)(unsafe.Pointer(C.cgo_gateway_free_func)) // Register our Create and Destroy functions in a Go map, so the correct // function can be called when cgo_gateway__func is called. CreateFuncRegistry[createFunc.MethodData] = createFunc.CreateFunc FreeFuncRegistry[createFunc.MethodData] = createFunc.FreeFunc DestroyFuncRegistry[destroyFunc.MethodData] = destroyFunc.DestroyFunc FreeFuncRegistry[destroyFunc.MethodData] = destroyFunc.FreeFunc // Register the class with Godot. C.go_godot_nativescript_register_tool_class( n.api, n.handle, C.CString(name), C.CString(base), createFunc.getBase(), destroyFunc.getBase(), ) } // RegisterMethod will register the given method with Godot and associate it with // the given class name. The name parameter is the name of the class you wish to // attach this method to. The funcName parameter is the name of the function you // want to register. The attributes and method are what will actually be called // when Godot calls the method on the object. func (n *nativeScript) RegisterMethod(name, funcName string, attributes *MethodAttributes, method *InstanceMethod) { // Construct the C struct based on the Go struct wrappers attributes.base.rpc_type = attributes.RPCType.getBase() method.base.method = (C.method)(unsafe.Pointer(C.cgo_gateway_method_func)) method.base.method_data = unsafe.Pointer(C.CString(method.MethodData)) method.base.free_func = (C.free_func)(unsafe.Pointer(C.cgo_gateway_free_func)) // Register the Method function in a Go map, so the correct function can // be called when cgo_gateway__func is called. MethodFuncRegistry[method.MethodData] = method.Method FreeFuncRegistry[method.MethodData] = method.FreeFunc // Register the method with Godot. C.go_godot_nativescript_register_method( n.api, n.handle, C.CString(name), C.CString(funcName), attributes.getBase(), method.getBase(), ) } // RegisterProperty will register the given property with Godot and associate it // with the given class name. The name parameter is the name of the class you wish // to attach this property to. The path is the name of the property itself. The // attributes and setter/getter methods are what will be called when Godot gets // or sets this property. //func (n *nativeScript) RegisterProperty(name, path string, attributes *C.godot_property_attributes, setFunc C.godot_property_set_func, getFunc C.godot_property_get_func) { func (n *nativeScript) RegisterProperty(name, path string, attributes *PropertyAttributes, setFunc *InstancePropertySet, getFunc *InstancePropertyGet) { // Construct the C struct based on the attributes Go wrapper var attr C.godot_property_attributes attributes.base = &attr attributes.base.rset_type = attributes.RsetType.getBase() attributes.base._type = attributes.Type.getBase() attributes.base.hint = attributes.Hint.getBase() attributes.base.hint_string = *(attributes.HintString.getBase()) attributes.base.usage = attributes.Usage.getBase() attributes.base.default_value = *(attributes.DefaultValue.getBase()) // Construct the C struct based on the setFunc Go wrapper setFunc.base.set_func = (C.set_property_func)(unsafe.Pointer(C.cgo_gateway_property_set_func)) setFunc.base.method_data = unsafe.Pointer(C.CString(setFunc.MethodData)) setFunc.base.free_func = (C.free_func)(unsafe.Pointer(C.cgo_gateway_free_func)) // Construct the C struct based on the getFunc Go wrapper getFunc.base.get_func = (C.get_property_func)(unsafe.Pointer(C.cgo_gateway_property_get_func)) getFunc.base.method_data = unsafe.Pointer(C.CString(getFunc.MethodData)) getFunc.base.free_func = (C.free_func)(unsafe.Pointer(C.cgo_gateway_free_func)) // Register the set/get property functions in a Go map, so the correct function can // be called when cgo_gateway__func is called. SetPropertyFuncRegistry[setFunc.MethodData] = setFunc.SetFunc GetPropertyFuncRegistry[getFunc.MethodData] = getFunc.GetFunc FreeFuncRegistry[setFunc.MethodData] = setFunc.FreeFunc FreeFuncRegistry[getFunc.MethodData] = getFunc.FreeFunc // Register the property with Godot. C.go_godot_nativescript_register_property( n.api, n.handle, C.CString(name), C.CString(path), attributes.getBase(), setFunc.getBase(), getFunc.getBase(), ) } // RegisterSignal will register the given signal with Godot. func (n *nativeScript) RegisterSignal(name string, signal *Signal) { // Construct the C struct based on the signal Go wrapper var base C.godot_signal signal.base = (*C.godot_signal)(unsafe.Pointer(&base)) signal.base.name = *(signal.Name.getBase()) signal.base.num_args = signal.NumArgs.getBase() signal.base.num_default_args = signal.NumDefaultArgs.getBase() // Build the arguments // argsArray := C.go_godot_signal_argument_build_array(C.int(signal.NumArgs)) argsArray := make([]*C.godot_signal_argument, int(signal.NumArgs)) for i, arg := range signal.Args { cArg := C.go_godot_new_signal_argument() (*cArg).name = *(arg.Name.getBase()) (*cArg)._type = arg.Type.getBase() (*cArg).default_value = *(arg.DefaultValue.getBase()) (*cArg).hint = arg.Hint.getBase() (*cArg).hint_string = *(arg.HintString.getBase()) (*cArg).usage = arg.Usage.getBase() argsArray[i] = cArg } signal.base.args = argsArray[0] // Build the default arguments variantArray := C.go_godot_variant_build_array(C.int(signal.NumDefaultArgs)) for i, variant := range signal.DefaultArgs { C.go_godot_variant_add_element(variantArray, variant.getBase(), C.int(i)) } signal.base.default_args = *(**C.godot_variant)(unsafe.Pointer(&variantArray)) // Register the signal with Godot. C.go_godot_nativescript_register_signal( n.api, n.handle, C.CString(name), signal.getBase(), ) } // nativeScriptInit will be called when `godot_nativescript_init` is called by // Godot. You can use `SetNativeScriptInit` to set the function that will be called // when NativeScript initializes. var nativeScriptInit = []func(){} // SetNativeScriptInit will configure the given function to be called when // `godot_nativescript_init` is called by Godot upon NativeScript initialization. // This is used so you can define a function that will run to register all of the // classes that you want exposed to Godot. func SetNativeScriptInit(initFunc ...func()) { nativeScriptInit = append(nativeScriptInit, initFunc...) } /*------------------------------------------------------------------------------ // Exported C Functions // // The methods below are special C exported functions. They can be called by // Godot directly or by one of the C gateway functions defined in // nativescript.c. // //----------------------------------------------------------------------------*/ /** Script entry (Registering all the classes and stuff) **/ // godot_nativescript_init is the script's entrypoint. It is called by Godot // when a script is loaded. It is responsible for registering all the classes, // etc. The `unsafe.Pointer` type is used to represent a void C pointer. //export godot_nativescript_init func godot_nativescript_init(hdl unsafe.Pointer) { if debug { log.Println("Initializing NativeScript") } NativeScript.handle = hdl // Call the user-defined nativeScriptInit function if nativeScriptInit == nil { err := "NativeScript initialization function was not set! Use `gdnative.SetNativeScriptInit` to define the function that will run to register classes." log.Println(err) Log.Error(err) return } // Loop through any defined nativeScriptInit methods and execute them. for _, init := range nativeScriptInit { init() } } // This is a native Go function that is callable from C. It is called by the // gateway functions defined in nativescript.c. It will be ultimately called by // Godot, where it will pass us the Godot object and the MethodData defined in // CreateFunc. We will need to return UserData, which can be used to track the // actual instance that was created. //export go_create_func func go_create_func(godotObject *C.godot_object, methodData unsafe.Pointer) unsafe.Pointer { // Convert the method data into a Go string. methodDataString := unsafeToGoString(methodData) if debug { log.Println("Create function called for:", methodDataString) } // Look up the creation function in our CreateFuncRegistry for the function // to call. constructor := CreateFuncRegistry[methodDataString] // Call the constructor and return the user data string. The user data // returned by the create func will be passed to the method function as // userData. userData := constructor(Object{base: godotObject}, methodDataString) return unsafe.Pointer(C.CString(userData)) } // This is a native Go function that is callable from C. It is called by the // gateway functions defined in nativescript.c. It will use the userData passed to it, // which is a CString of the instance id, which we can use to delete the instance // from the instance registry. This will make the instance available to be garbage // collected. //export go_destroy_func func go_destroy_func(godotObject *C.godot_object, methodData unsafe.Pointer, userData unsafe.Pointer) { // Convert the method data and user data into a Go string methodDataString := unsafeToGoString(methodData) userDataString := unsafeToGoString(userData) if debug { log.Println("Destroy function called for:", methodDataString) } // Look up the destroy function in our DestroyFuncRegistry for the function // to call. destructor := DestroyFuncRegistry[methodDataString] // Call the destructor function. We pass the methodData and userData to // the destructor so it knows which class and instance to destroy. destructor(Object{base: godotObject}, methodDataString, userDataString) } //export go_free_func func go_free_func(methodData unsafe.Pointer) { // Convert the method data and user data into a Go string methodDataString := unsafeToGoString(methodData) if debug { log.Println("Free function called for:", methodDataString) } // Look up the free function in our FreeFuncRegistry for the function // to call. freer := FreeFuncRegistry[methodDataString] // Call the free function. We pass the methodData to the free // function so it knows which class to free. freer(methodDataString) } // This is a native Go function that is callable from C. It is called by the // gateway functions defined in nativescript.c. //export go_method_func func go_method_func(godotObject *C.godot_object, methodData unsafe.Pointer, userData unsafe.Pointer, numArgs C.int, args **C.godot_variant) C.godot_variant { // Convert the method data and user data into a Go string methodDataString := unsafeToGoString(methodData) userDataString := unsafeToGoString(userData) // Create a slice of Variants for the arguments variantArgs := []Variant{} // Get the size of each godot_variant object pointer. if debug { log.Println(" Getting size of argument pointer") } size := unsafe.Sizeof(*args) // Panic if something's wrong. if int(numArgs) > 50 { panic("Too many arguments. Invalid method.") } // If we have arguments, append the first argument. if int(numArgs) > 0 { arg := args // Loop through all our arguments. for i := 0; i < int(numArgs); i++ { // Convert the variant into a Go Variant variant := Variant{base: *arg} // Append the variant to our list of variants variantArgs = append(variantArgs, variant) // Convert the pointer into a uintptr so we can perform arithmetic on it. arrayPtr := uintptr(unsafe.Pointer(arg)) // Add the size of the godot_variant pointer to our array pointer to get the position // of the next argument. arg = (**C.godot_variant)(unsafe.Pointer(arrayPtr + size)) } } // Look up the method function in our MethodFuncRegistry for the function // to call. method := MethodFuncRegistry[methodDataString] // Call the method ret := method(Object{base: godotObject}, methodDataString, userDataString, int(numArgs), variantArgs) return *ret.getBase() } // This is a native Go function that is callable from C. It is called by the // gateway functions defined in nativescript.c. //export go_set_property_func func go_set_property_func(godotObject *C.godot_object, methodData unsafe.Pointer, userData unsafe.Pointer, property *C.godot_variant) { // Convert the method data and user data into a Go string methodDataString := unsafeToGoString(methodData) userDataString := unsafeToGoString(userData) // Convert the property into a Go variant variant := Variant{base: property} // Look up the set property function in our SetPropertyFuncRegistry for // the function to call. setFunc := SetPropertyFuncRegistry[methodDataString] // Call the method setFunc(Object{base: godotObject}, methodDataString, userDataString, variant) } // This is a native Go function that is callable from C. It is called by the // gateway functions defined in nativescript.c. //export go_get_property_func func go_get_property_func(godotObject *C.godot_object, methodData unsafe.Pointer, userData unsafe.Pointer) C.godot_variant { // Convert the method data and user data into a Go string methodDataString := unsafeToGoString(methodData) userDataString := unsafeToGoString(userData) // Look up the get property function in our GetPropertyFuncRegistry for // the function to call. getFunc := GetPropertyFuncRegistry[methodDataString] // Call the method ret := getFunc(Object{base: godotObject}, methodDataString, userDataString) return *ret.getBase() }