pimpam.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. // Copyright © 2019 - 2020 Oscar Campos <oscar.campos@thepimpam.com>
  2. // Copyright © 2017 - William Edwards
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License
  15. package gdnative
  16. import (
  17. "fmt"
  18. "log"
  19. "strings"
  20. )
  21. // instances is a map of our created Godot classes. This will be
  22. // populated when Godot calls the CreateFunc
  23. var instances = map[string]*Class{}
  24. // Objectable is the interface every Godot Object has to implement
  25. type Objectable interface {
  26. BaseClass() string
  27. SetOwner(Object)
  28. Owner() Object
  29. }
  30. // GDSignal is a NativeScript registrable signal
  31. type GDSignal struct {
  32. name string
  33. signalName string
  34. signal *Signal
  35. }
  36. // Method is a NativeScript registrable function
  37. type Method struct {
  38. name string
  39. funcName string
  40. attributes *MethodAttributes
  41. method *InstanceMethod
  42. }
  43. // Property is a NativeScript registrable class property
  44. type Property struct {
  45. Value Variant
  46. name string
  47. propertyName string
  48. attributes *PropertyAttributes
  49. setFunc *InstancePropertySet
  50. getFunc *InstancePropertyGet
  51. }
  52. // Class is a NativeScript registrable class
  53. type Class struct {
  54. isTool bool
  55. name string
  56. base string
  57. createFunc *InstanceCreateFunc
  58. destroyFunc *InstanceDestroyFunc
  59. methods []Method
  60. properties []Property
  61. signals []GDSignal
  62. }
  63. // RegisterNewGodotClass creates a new ready to go Godot class for us and registers it with in Godot
  64. func RegisterNewGodotClass(isTool bool, name, base string, constructor *InstanceCreateFunc, destructor *InstanceDestroyFunc,
  65. methods []Method, properties []Property, signals []GDSignal) {
  66. // create a new Class value and registers it
  67. godotClass := Class{isTool, name, base, constructor, destructor, methods, properties, signals}
  68. godotClass.register()
  69. }
  70. // Register registers a Class value with in Godot
  71. func (c *Class) register() {
  72. // if the class constructor and destructor are not defined create generic ones
  73. if c.createFunc == nil {
  74. c.createFunc = c.createGenericConstructor()
  75. }
  76. if c.destroyFunc == nil {
  77. c.destroyFunc = c.createGenericDestructor()
  78. }
  79. // we register the class first
  80. if c.isTool {
  81. NativeScript.RegisterToolClass(c.name, c.base, c.createFunc, c.destroyFunc)
  82. } else {
  83. NativeScript.RegisterClass(c.name, c.base, c.createFunc, c.destroyFunc)
  84. }
  85. // then we iterate over every defined method and register them as well
  86. for _, method := range c.methods {
  87. method.register()
  88. }
  89. // then iterate over any defined property and register them
  90. for _, property := range c.properties {
  91. if err := property.register(); err != nil {
  92. panic(fmt.Errorf("could not register class properties: %w", err))
  93. }
  94. }
  95. // finally iterate over any defined signal and register them
  96. for _, signal := range c.signals {
  97. signal.register(c.name)
  98. }
  99. }
  100. // CreateConstructor creates an InstanceCreateFunc value using the given CreateFunc and return it back
  101. func CreateConstructor(className string, fn CreateFunc) InstanceCreateFunc {
  102. constructor := InstanceCreateFunc{
  103. CreateFunc: fn,
  104. MethodData: className,
  105. FreeFunc: func(methodData string) {},
  106. }
  107. return constructor
  108. }
  109. // CreateDestructor creates an InstanceDestroyFunc value using the given DestroyFunc and return it back
  110. func CreateDestructor(className string, fn DestroyFunc) InstanceDestroyFunc {
  111. destructor := InstanceDestroyFunc{
  112. DestroyFunc: fn,
  113. MethodData: className,
  114. FreeFunc: func(methodData string) {},
  115. }
  116. return destructor
  117. }
  118. // creates a generic constructor for any given class
  119. func (c *Class) createGenericConstructor() *InstanceCreateFunc {
  120. constructorFunc := func(object Object, methodData string) string {
  121. // use the Godot object ID as ID for this class
  122. id := object.ID()
  123. Log.Println(fmt.Sprintf("Creating Go generic class %s(%s) constructor with ID %s", c.name, c.base, id))
  124. // use the class pointer address as the instance ID
  125. instances[id] = c
  126. return id
  127. }
  128. createFunc := CreateConstructor(c.name, constructorFunc)
  129. return &createFunc
  130. }
  131. // creates a generic destructor for any given class
  132. func (c *Class) createGenericDestructor() *InstanceDestroyFunc {
  133. destructorFunc := func(object Object, methodData, userData string) {
  134. Log.Println(fmt.Sprintf("Destroying %s value with ID: %s", c.name, userData))
  135. delete(instances, userData)
  136. }
  137. destroyFunc := CreateDestructor(c.name, destructorFunc)
  138. return &destroyFunc
  139. }
  140. // NewGodotSignal creates a new ready to go Godot signal for us and return it back
  141. func NewGodotSignal(className, name string, args []SignalArgument, defaults []Variant) GDSignal {
  142. // create a new GDSignal value
  143. godotSignal := GDSignal{
  144. name: className,
  145. signalName: name,
  146. signal: &Signal{
  147. Name: String(name),
  148. NumArgs: Int(len(args)),
  149. NumDefaultArgs: Int(len(defaults)),
  150. Args: args,
  151. DefaultArgs: defaults,
  152. },
  153. }
  154. return godotSignal
  155. }
  156. // registers a Signal value with in Godot
  157. func (s *GDSignal) register(name string) {
  158. NativeScript.RegisterSignal(s.name, s.signal)
  159. }
  160. // NewGodotMethod creates a new ready to go Godot method for us and return it back
  161. func NewGodotMethod(className, name string, method MethodFunc) Method {
  162. // create a new Method value
  163. godotMethod := Method{
  164. className,
  165. name,
  166. &MethodAttributes{
  167. RPCType: MethodRpcModeDisabled,
  168. },
  169. &InstanceMethod{
  170. Method: method,
  171. MethodData: name,
  172. FreeFunc: func(methodData string) {},
  173. },
  174. }
  175. return godotMethod
  176. }
  177. // registers a Method value with in Godot
  178. func (m *Method) register() {
  179. NativeScript.RegisterMethod(m.name, m.funcName, m.attributes, m.method)
  180. }
  181. // NewGodotProperty creates a new ready to go Godot property, add it to the given class and return it
  182. func NewGodotProperty(className, name, hint, hintString, usage, rset string,
  183. setFunc *InstancePropertySet, getFunc *InstancePropertyGet) Property {
  184. // create ok boolean re-usable helper value
  185. var ok bool
  186. // create a new PropertyAttributes value and fill it
  187. var attributes PropertyAttributes
  188. attributes.HintString = String(hintString)
  189. attributes.DefaultValue = NewVariantNil()
  190. if hint != "" {
  191. hintKey := hint
  192. if strings.HasPrefix(hintKey, "gdnative.") {
  193. hintKey = hint[9:]
  194. } else if !strings.HasPrefix(hintKey, "PropertyHint") {
  195. hintKey = fmt.Sprintf("PropertyHint%s", hint)
  196. }
  197. if attributes.Hint, ok = PropertyHintLookupMap[hintKey]; !ok {
  198. var allowed []string
  199. for key := range PropertyHintLookupMap {
  200. allowed = append(allowed, strings.Replace(key, "PropertyHint", "", 1))
  201. }
  202. panic(fmt.Sprintf("unknown property hint %q, allowed types: %s", hint, strings.Join(allowed, ", ")))
  203. }
  204. } else {
  205. attributes.Hint = PropertyHintNone
  206. }
  207. if usage != "" {
  208. usageKey := usage
  209. if strings.HasPrefix(usageKey, "gdnative.") {
  210. usageKey = usage[9:]
  211. } else if !strings.HasPrefix(usageKey, "PropertyUsage") {
  212. usageKey = fmt.Sprintf("PropertyUsage%s", usage)
  213. }
  214. if attributes.Usage, ok = PropertyUsageFlagsLookupMap[usageKey]; !ok {
  215. var allowed []string
  216. for key := range PropertyUsageFlagsLookupMap {
  217. allowed = append(allowed, strings.Replace(key, "PropertyUsage", "", 1))
  218. }
  219. panic(fmt.Sprintf("unknown property usage %q, allowed types: %s", usage, strings.Join(allowed, ", ")))
  220. }
  221. } else {
  222. attributes.Usage = PropertyUsageDefault
  223. }
  224. if rset != "" {
  225. rsetType := rset
  226. if strings.HasPrefix(rsetType, "gdnative.") {
  227. rsetType = rset[9:]
  228. } else if !strings.HasPrefix(rsetType, "MethodRpcMode") {
  229. rsetType = fmt.Sprintf("MethodRpcMode%s", rset)
  230. }
  231. if attributes.RsetType, ok = MethodRpcModeLookupMap[rsetType]; !ok {
  232. var validTypes string
  233. for key := range MethodRpcModeLookupMap {
  234. validTypes = fmt.Sprintf("%s %s", validTypes, strings.Replace(key, "MethodRpcMode", "", 1))
  235. }
  236. panic(fmt.Sprintf("unknown rset %q, allowed types: %s", rset, validTypes))
  237. }
  238. } else {
  239. attributes.RsetType = MethodRpcModeDisabled
  240. }
  241. // create a new Property value
  242. godotProperty := Property{
  243. NewVariantNil(),
  244. className,
  245. name,
  246. &attributes,
  247. setFunc,
  248. getFunc,
  249. }
  250. return godotProperty
  251. }
  252. // registers a property within the class/godot
  253. func (p *Property) register() error {
  254. // if set and get functions are not defined generate generic ones
  255. if p.setFunc == nil {
  256. p.setFunc = p.CreateGenericSetter()
  257. }
  258. if p.setFunc == nil || p.getFunc == nil {
  259. return fmt.Errorf("you can not register a property that does not defines both setter and getter functions")
  260. }
  261. NativeScript.RegisterProperty(p.name, p.propertyName, p.attributes, p.setFunc, p.getFunc)
  262. return nil
  263. }
  264. // creates a generic setter method to set property values if none is provided
  265. func (p *Property) CreateGenericSetter() *InstancePropertySet {
  266. propertySetter := func(object Object, classProperty, instanceString string, property Variant) {
  267. Log.Println(fmt.Sprintf("Creating Go generic property setter for %s.%s", p.name, p.propertyName))
  268. p.Value = property
  269. }
  270. instancePropertySet := InstancePropertySet{
  271. SetFunc: propertySetter,
  272. MethodData: fmt.Sprintf("%s::%s", p.name, p.propertyName),
  273. FreeFunc: func(methodData string) {},
  274. }
  275. return &instancePropertySet
  276. }
  277. // created a generic getter method to get property values if none is provided
  278. func (p *Property) CreateGenericGetter() *InstancePropertyGet {
  279. propertyGetter := func(object Object, classProperty, instanceString string) Variant {
  280. log.Println(fmt.Sprintf("Creating Go generic property getter for %s.%s", p.name, p.propertyName))
  281. return p.Value
  282. }
  283. instancePropertyGet := InstancePropertyGet{
  284. GetFunc: propertyGetter,
  285. MethodData: fmt.Sprintf("%s::%s", p.name, p.propertyName),
  286. FreeFunc: func(methodData string) {},
  287. }
  288. return &instancePropertyGet
  289. }
  290. // SetSetter sets the setter on a Property value
  291. func (p *Property) SetSetter(setter *InstancePropertySet) {
  292. p.setFunc = setter
  293. }
  294. // SetGetter sets the getter on a Property value
  295. func (p *Property) SetGetter(getter *InstancePropertyGet) {
  296. p.getFunc = getter
  297. }
  298. // GetName returns back the property name
  299. func (p *Property) GetName() string {
  300. return p.propertyName
  301. }