// 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 import ( "fmt" "strings" "unicode" ) // Registrable is the interface external code communicates with registryClass type Registrable interface { GetBase() string GetConstructor() string GetDestructor() string GetMethods() []string GetProperties() []string SetConstructor(*registryConstructor) SetDestructor(*registryDestructor) Constructor() string Destructor() string AddMethod(*registryMethod) AddMethods([]*registryMethod) Methods() []*registryMethod AddSignal(*registrySignal) AddSignals([]*registrySignal) Signals() []*registrySignal AddProperties([]*registryProperty) Properties() []*registryProperty } type registryClass struct { base, alias string constructor *registryConstructor destructor *registryDestructor methods []*registryMethod properties []*registryProperty signals []*registrySignal } // GetBase returns back the Godot base class for this type as a string func (rc *registryClass) GetBase() string { return rc.base } // Alias returns the class alias func (rc *registryClass) Alias() string { return rc.alias } // GetConstructor returns back this type constructor as a string func (rc *registryClass) GetConstructor() string { if rc.constructor != nil { return fmt.Sprintf("func %s() *%s", rc.constructor.customFunc, rc.constructor.class) } return "" } // SetConstructor sets this type constructor func (rc *registryClass) SetConstructor(constructor *registryConstructor) { if constructor != nil { rc.constructor = constructor } } // HasConstructor returns true if this type has custom constructor func (rc *registryClass) HasConstructor() bool { return rc.constructor != nil } // Constructor returns the constructor custom function func (rc *registryClass) Constructor() string { if rc.constructor != nil { return rc.constructor.customFunc } return "" } // GetDestructor returns back this type destructor as a string func (rc *registryClass) GetDestructor() string { if rc.destructor != nil { return fmt.Sprintf("func %s()", rc.destructor.customFunc) } return "" } // SetDestructor sets this type destructor func (rc *registryClass) SetDestructor(destructor *registryDestructor) { if destructor != nil { rc.destructor = destructor } } // HasDestructor returns true if this type has custom destructor func (rc *registryClass) HasDestructor() bool { return rc.destructor != nil } // Destructor returns the destructor custom function func (rc *registryClass) Destructor() string { if rc.destructor != nil { return rc.destructor.customFunc } return "" } // GetMethods returns back this type exported methods as strings func (rc *registryClass) GetMethods() []string { methods := []string{} for _, method := range rc.methods { methods = append(methods, fmt.Sprintf("func %s(%s) %s", method.name, method.GetParams(), method.GetReturnValues())) } return methods } // Methods returns back this type list of registryMethods func (rc *registryClass) Methods() []*registryMethod { return rc.methods } // AddMethods adds a list of methods for this type func (rc *registryClass) AddMethods(methods []*registryMethod) { for i := range methods { rc.AddMethod(methods[i]) } } // AddMethod adds a method to this type func (rc *registryClass) AddMethod(method *registryMethod) { if method != nil { rc.methods = append(rc.methods, method) } } // Signals returns back this type list of registrySignals func (rc *registryClass) Signals() []*registrySignal { return rc.signals } // AddSignals adds a list of signals to this type func (rc *registryClass) AddSignals(signals []*registrySignal) { for i := range signals { rc.AddSignal(signals[i]) } } // AddSignal adds a signal to this type func (rc *registryClass) AddSignal(signal *registrySignal) { if signal != nil { rc.signals = append(rc.signals, signal) } } // GetProperties returns back this class properties as strings func (rc *registryClass) GetProperties() []string { properties := []string{} for _, property := range rc.properties { properties = append(properties, property.name) } return properties } // AddProperties adds a list of properties to this type func (rc *registryClass) AddProperties(properties []*registryProperty) { for i := range properties { rc.AddProperty(properties[i]) } } // AddProperty adds a property to this type func (rc *registryClass) AddProperty(property *registryProperty) { if property != nil { rc.properties = append(rc.properties, property) } } // Properties returns back this class list of registryProperty func (rc *registryClass) Properties() []*registryProperty { return rc.properties } type registryConstructor struct { class, customFunc string } type registryDestructor struct { class, customFunc string } type registryMethod struct { class, name, alias string params []*registryMethodParam returnValues []*registryMethodReturnValue } // GetName returns the method name func (rm *registryMethod) GetName() string { return rm.name } // GodotName returns the Godot name for this virtual method func (rm *registryMethod) GodotName() string { if rm.name[0] == 'V' && unicode.IsUpper(rune(rm.name[1])) { return strings.ToLower(fmt.Sprintf("_%s", rm.name[1:])) } return rm.name } // Alias returns the method alias func (rm *registryMethod) Alias() string { return rm.alias } // GetParams returns this type method params as a string func (rm *registryMethod) GetParams() string { pairs := []string{} for _, param := range rm.params { pairs = append(pairs, fmt.Sprintf("%s %s", param.name, param.kind)) } return strings.Join(pairs, ", ") } // GetReturnValues returns this type method return values as a string func (rm *registryMethod) GetReturnValues() string { values := []string{} for _, value := range rm.returnValues { values = append(values, value.kind) } return strings.Join(values, ", ") } // Arguments returns a slice of this method arguments structures func (rm *registryMethod) Arguments() []*registryMethodParam { return rm.params } // HasReturns returns true if this method has return values, otherwise returns false func (rm *registryMethod) HasReturns() bool { return len(rm.returnValues) > 0 } // FunctionCallWithParams returns a string representing how this method should be called func (rm *registryMethod) FunctionCallWithParams() string { arguments := make([]string, len(rm.params)) for i, arg := range rm.params { arguments[i] = arg.name } return fmt.Sprintf("%s(%s)", rm.name, strings.Join(arguments, ", ")) } // NewVariantType returns the right NewVariant method from gdnative for our return type func (rm *registryMethod) NewVariantType() string { variant := "NewVariant%s(gdnative.%s(value))" conversions := map[string]string{ "bool": fmt.Sprintf(variant, "Bool", "Bool"), "uint": fmt.Sprintf(variant, "Uint", "Uint64T"), "int": fmt.Sprintf(variant, "Int", "Int64T"), "float64": fmt.Sprintf(variant, "Real", "Double"), "string": fmt.Sprintf(variant, "String", "String"), } retLength := len(rm.returnValues) if retLength == 1 { result, ok := conversions[rm.returnValues[0].kind] if !ok { result = "value" } return result } if retLength >= 2 && retLength <= 3 { valid := make([]bool, retLength) for i, val := range rm.returnValues { switch val.kind { case "float32", "float64", "gdnative.Double", "gdnative.Real": valid[i] = true } } allValid := true for _, b := range valid { if !b { allValid = false break } } if allValid { return fmt.Sprintf( variant, fmt.Sprintf("Vector%d", retLength), fmt.Sprintf("NewVector%d", retLength), ) } } return "value" } type registryProperty struct { name, alias, kind, gdnativeKind, hint, hintString, usage, rset, setFunc, getFunc string } // Name returns the name of the property back func (rp *registryProperty) Name() string { return rp.name } // Kind returns the property type back func (rp *registryProperty) Kind() string { return rp.gdnativeKind } // Alias returns the property alias back func (rp *registryProperty) Alias() string { return rp.alias } // Hint returns the hint of the property back func (rp *registryProperty) Hint() string { return rp.hint } // HintString returns the hint string of the property back func (rp *registryProperty) HintString() string { return rp.hintString } // Usage returns the usage of the property back func (rp *registryProperty) Usage() string { return rp.usage } // RsetType returns the rset of the property back func (rp *registryProperty) RsetType() string { return rp.rset } // SetConvert writes right syntax for conversion from gdnative.Variant into Go type func (rp *registryProperty) SetConvert() string { switch rp.gdnativeKind { case "gdnative.Int": return "gdnative.Int(property.AsInt())" default: return fmt.Sprintf("property.As%s()", strings.Replace(rp.gdnativeKind, "gdnative.", "", -1)) } } // GetConvert writes right syntax for conversion from Go type into gdnative.Variant func (rp *registryProperty) GetConvert() string { switch rp.gdnativeKind { case "gdnative.Int": return fmt.Sprintf("gdnative.NewVariant%s(gdnative.Int64T(class.class.%s))", rp.gdnativeKind[9:], rp.name) default: return fmt.Sprintf("gdnative.NewVariant%s(class.class.%s)", rp.gdnativeKind[9:], rp.name) } } // SetFunc returns this property set function or default one func (rp *registryProperty) SetFunc(class, instance string) string { if rp.setFunc == "" { rp.setFunc = fmt.Sprintf(`gdnative.NewGodotPropertySetter("%s", %s, %sInstances)`, class, rp.kind, instance) } return rp.setFunc } // GetFunc returns this property Get function or default one func (rp *registryProperty) GetFunc(class, instance string) string { if rp.getFunc == "" { rp.getFunc = fmt.Sprintf(`gdnative.NewGodotPropertyGetter("%s", %s, %sInstances)`, class, rp.kind, instance) } return rp.getFunc } type registrySignal struct { name, args, defaults string } // Name returns this signal name back func (rs *registrySignal) Name() string { return rs.name } // Args returns this signal args back func (rs *registrySignal) Args() string { return rs.args } // Defaults returns this signal defaults back func (rs *registrySignal) Defaults() string { return rs.defaults } type registryMethodParam struct { name, kind string } // Name returns this param name func (rmp *registryMethodParam) Name() string { return rmp.name } // Kind returns this param kind func (rmp *registryMethodParam) Kind() string { return rmp.kind } // ConvertFunction returns the right GDNative 'As' function for this param kind as a string func (rmp *registryMethodParam) ConvertFunction() string { conversions := map[string]string{ "bool": "AsBool()", "uint": "AsUint()", "int": "AsInt()", "float64": "AsReal()", "string": "AsString()", "vector2": "AsVector2()", "vector3": "AsVector3()", "rect2": "AsRect2()", "transform2d": "AsTransform2D()", "plane": "AsPlane()", "quat": "AsQuat()", "aabb": "AsAabb()", "basis": "AsBasis()", "transform": "AsTransform()", "color": "AsColor()", "nodepath": "AsNodePath()", "rid": "AsRid()", "object": "AsObject()", "dictionary": "AsDictionary()", "arraytype": "AsArray()", "arraytype_byte": "AsPoolByteArray()", "arraytype_int": "AsPoolIntArray()", "arraytype_float": "AsPoolRealArray()", "arraytype_string": "AsPoolStringArray()", "arraytype_vector2": "AsPoolVector2Array()", "arraytype_vector3": "AsPoolVector3Array()", "arraytype_color": "AsPoolColorArray()", } value, ok := conversions[rmp.kind] if ok { return value } switch rmp.kind { case "float32": value = conversions["float64"] case "int8", "int16", "int32", "int64", "byte": value = conversions["int"] case "uint8", "uint16", "uint32", "uint64": value = conversions["uint"] case "gdnative.Float", "gdnative.Double": value = conversions["float64"] case "gdnative.Int64T", "gdnative.SignedChar": value = conversions["int"] case "gdnative.Uint", "gdnative.Uint8T", "gdnative.Uint32T", "gdnative.Uint64T": value = conversions["uint"] case "gdnative.String", "gdnative.Char", "gdnative.WcharT": value = conversions["string"] default: if strings.HasPrefix(rmp.kind, "gdnative.") { value = rmp.kind } if strings.HasPrefix(rmp.kind, "godot.") || strings.HasPrefix(rmp.kind, "*godot.") { value = conversions["object"] } if strings.Contains(rmp.kind, "ArrayType") { value = conversions[parseArrayType(rmp.kind)] } if strings.Contains(rmp.kind, "MapType") { value = conversions["dictionary"] } } if value == "" { value = conversions["object"] } return value } type registryMethodReturnValue struct { kind string } func parseArrayType(array string) string { var arrayType string var openBracket bool var openBracketCount int for i := 0; i < len(array); i++ { if openBracket { if array[i] == ']' { openBracketCount-- if openBracketCount == 0 { openBracket = false continue } } if array[i] == '[' { openBracketCount++ } arrayType += string(array[i]) continue } if array[i] == '[' { openBracket = true openBracketCount = 1 } } return fmt.Sprintf("arraytype_%s", strings.ReplaceAll(arrayType, "gdnative.", "")) }