├── .gitignore ├── LICENSE.md ├── README.md ├── doc └── exports.md ├── env.yml ├── example ├── .import │ ├── icon.png-487276ed1e3a0c39cad0279d744ee560.md5 │ └── icon.png-487276ed1e3a0c39cad0279d744ee560.stex ├── Label.rb ├── Main.gd ├── Main.tscn ├── Node.gd ├── bin │ ├── godot-ruby.gdnlib │ └── godot-ruby.so ├── default_env.tres ├── hello.rb ├── icon.png ├── icon.png.import └── project.godot ├── lib ├── godot.rb └── godot │ ├── attached_script.rb │ ├── class.rb │ └── object.rb ├── makefile ├── src ├── debug.c ├── debug.h ├── godot-ruby.c └── pluginscript.c └── util ├── generate.rb ├── generator.rb └── generator ├── class.rb ├── class ├── base.rb ├── heap.rb ├── stack.rb └── struct.rb ├── classes.rb ├── classes ├── array.rb ├── dictionary.rb ├── node_path.rb ├── object.rb ├── pool_string_array.rb └── string.rb ├── function.rb ├── function └── argument.rb ├── type.rb ├── type ├── alias.rb ├── base.rb ├── heap.rb ├── heap_pointer.rb ├── stack.rb ├── stack_pointer.rb └── struct.rb ├── types.rb └── types ├── godot_bool.rb ├── godot_int.rb ├── godot_object_pointer.rb ├── godot_real.rb ├── godot_variant.rb ├── godot_variant_pointer.rb ├── wchar_t.rb └── wchar_t_pointer.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.os 2 | bin/*.so 3 | src/generated.c 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Renxiang Cai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WARNING: This library has memory issues on mantaining godot objects. And I have forgotten how to fix them. Please avoid using! 2 | ------ 3 | 4 | # godot-ruby 5 | 6 | godot-ruby provides a Ruby language binding for Godot game engine. It is designed to be a drop-in replacement of GDScript. 7 | 8 | **However this project is mainly experimental due to serveral limitations. Use in production is highly not recommended.** 9 | 10 | # Building 11 | 12 | Modify the paths in `env.yml` and `makefile` then `make`. Currently only Linux (and Mac maybe) are supported because the workaround of limitation 3. uses `pthread.h`. 13 | 14 | # Implementation 15 | 16 | godot-ruby utilizes code generation to generate Godot apis and bind them to ruby side. By defining [C types](https://github.com/CicholGricenchos/godot-ruby/blob/master/util/generator/types.rb) and [Ruby classes](https://github.com/CicholGricenchos/godot-ruby/blob/master/util/generator/classes.rb), the apis can be automatically generated, as [generated.c](https://gist.github.com/CicholGricenchos/6698738dbbf753061b3d94eefbad5481). Therefore the code for this project is simple. 17 | 18 | The `util` folder contains code for generation. `lib` contains Ruby codes and `src` contains C codes. 19 | 20 | # Known limitations 21 | 22 | 1. godot-ruby is considerably slower than GDScript (about 15x in my benchmark, see https://github.com/touilleMan/godot-python/issues/101). This could be due to inefficient implementation of pluginscript api. 23 | 2. Ruby's method semantic is incompatible with the seperate properties and methods in GDScript since there are only methods in Ruby. 24 | For this we have to use `get` which looks weird: 25 | ```ruby 26 | rect2.position #GDScript 27 | rect2.get(:position) #Ruby 28 | ``` 29 | 3. CRuby vm cannot support multi-threading. When godot engine calls the vm in different threads it crashs, which happens in resource preview loading (could be safely bypassed). To solve this we can use MRuby instead or use a single thread to run Ruby vm. However these two all need thread and mutex support and GDNative api currently does not provide one. 30 | -------------------------------------------------------------------------------- /doc/exports.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | # If the exported value assigns a constant or constant expression, 3 | # the type will be inferred and used in the editor 4 | 5 | # export var number = 5 6 | export :number, :int, default: 5 7 | 8 | # Export can take a basic data type as an argument which will be 9 | # used in the editor 10 | 11 | # export(int) var number 12 | export :number, :int 13 | 14 | # Export can also take a resource type to use as a hint 15 | 16 | # export(Texture) var character_face 17 | export :character_face, :Texture 18 | # export(PackedScene) var scene_file 19 | export :scene_file, :PackedScene 20 | 21 | # Integers and strings hint enumerated values 22 | 23 | # Editor will enumerate as 0, 1 and 2 24 | # export(int, "Warrior", "Magician", "Thief") var character_class 25 | export :character_class, :int, enum: ["Warrior", "Magician", "Thief"] 26 | # Editor will enumerate with string names 27 | # export(String, "Rebecca", "Mary", "Leah") var character_name 28 | export :character_name, :String, enum: ["Rebecca", "Mary", "Leah"] 29 | 30 | # Named enum values 31 | 32 | # Editor will enumerate as THING_1, THING_2, ANOTHER_THING 33 | # enum NamedEnum {THING_1, THING_2, ANOTHER_THING = -1} 34 | # export (NamedEnum) var x 35 | not yet supported 36 | 37 | # Strings as paths 38 | 39 | # String is a path to a file 40 | # export(String, FILE) var f 41 | export :f, :file 42 | # String is a path to a directory 43 | # export(String, DIR) var f 44 | export :f, :dir 45 | # String is a path to a file, custom filter provided as hint 46 | # export(String, FILE, "*.txt") var f 47 | export :f, :file, extension: "*.txt" 48 | 49 | # Using paths in the global filesystem is also possible, 50 | # but only in tool scripts (see further below) 51 | 52 | # String is a path to a PNG file in the global filesystem 53 | # export(String, FILE, GLOBAL, "*.png") var tool_image 54 | export :tool_image, :global_file, extension: "*.png" 55 | # String is a path to a directory in the global filesystem 56 | # export(String, DIR, GLOBAL) var tool_dir 57 | export :tool_dir, :global_dir 58 | 59 | # The MULTILINE setting tells the editor to show a large input 60 | # field for editing over multiple lines 61 | # export(String, MULTILINE) var text 62 | export :text, :multiline 63 | 64 | # Limiting editor input ranges 65 | 66 | # Allow integer values from 0 to 20 67 | # export(int, 20) var i 68 | export :i, :int, range: (0..20) 69 | # Allow integer values from -10 to 20 70 | # export(int, -10, 20) var j 71 | export :j, :int, range: (-10..20) 72 | # Allow floats from -10 to 20, with a step of 0.2 73 | # export(float, -10, 20, 0.2) var k 74 | export :k, :float, range: (-10..20), step: 0.2 75 | # Allow values y = exp(x) where y varies between 100 and 1000 76 | # while snapping to steps of 20. The editor will present a 77 | # slider for easily editing the value. 78 | # export(float, EXP, 100, 1000, 20) var l 79 | export :l, :exp, range: (100..1000), step: 20 80 | 81 | # Floats with easing hint 82 | 83 | # Display a visual representation of the ease() function 84 | # when editing 85 | # export(float, EASE) var transition_speed 86 | export :transition_speed, :ease 87 | 88 | # Colors 89 | 90 | # Color given as Red-Green-Blue value 91 | # export(Color, RGB) var col # Color is RGB 92 | export :col, :rgb 93 | # Color given as Red-Green-Blue-Alpha value 94 | # export(Color, RGBA) var col # Color is RGBA 95 | export :col, :rgba 96 | 97 | # Another node in the scene can be exported too 98 | 99 | # export(NodePath) var node 100 | export :node, :NodePath 101 | ``` 102 | -------------------------------------------------------------------------------- /env.yml: -------------------------------------------------------------------------------- 1 | headers_path: /home/cichol/godot_headers 2 | -------------------------------------------------------------------------------- /example/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5: -------------------------------------------------------------------------------- 1 | source_md5="ae7e641067601e2184afcade49abd283" 2 | dest_md5="84511021bbc8c9d37c7f0f4d181de883" 3 | 4 | -------------------------------------------------------------------------------- /example/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onyxblade/godot-ruby/8072844a24b6c178e06248d613ab68e78a28b3da/example/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex -------------------------------------------------------------------------------- /example/Label.rb: -------------------------------------------------------------------------------- 1 | extends :Label 2 | 3 | def _ready 4 | set :text, 'zxx' 5 | end 6 | -------------------------------------------------------------------------------- /example/Main.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | func _on_Button_pressed(): 4 | var bunny = $Node.vector() 5 | #print(get_children()) 6 | print(bunny.texture) 7 | add_child(bunny) 8 | #$Label.text = 'zcvzxv' 9 | #print($Node.is_class("Node")) 10 | #print(HTTPClient.new().is_class("Reference")) 11 | 12 | -------------------------------------------------------------------------------- /example/Main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://Main.gd" type="Script" id=1] 4 | [ext_resource path="res://Label.rb" type="Script" id=2] 5 | [ext_resource path="res://hello.rb" type="Script" id=3] 6 | [ext_resource path="res://Node.gd" type="Script" id=4] 7 | 8 | [node name="main" type="Control" index="0"] 9 | anchor_left = 0.0 10 | anchor_top = 0.0 11 | anchor_right = 1.0 12 | anchor_bottom = 1.0 13 | rect_pivot_offset = Vector2( 0, 0 ) 14 | rect_clip_content = false 15 | mouse_filter = 0 16 | mouse_default_cursor_shape = 0 17 | size_flags_horizontal = 1 18 | size_flags_vertical = 1 19 | script = ExtResource( 1 ) 20 | 21 | [node name="Button" type="Button" parent="." index="0"] 22 | anchor_left = 0.0 23 | anchor_top = 0.0 24 | anchor_right = 0.0 25 | anchor_bottom = 0.0 26 | margin_left = 384.0 27 | margin_top = 233.0 28 | margin_right = 630.0 29 | margin_bottom = 289.0 30 | rect_pivot_offset = Vector2( 0, 0 ) 31 | rect_clip_content = false 32 | focus_mode = 2 33 | mouse_filter = 0 34 | mouse_default_cursor_shape = 0 35 | size_flags_horizontal = 1 36 | size_flags_vertical = 1 37 | toggle_mode = false 38 | enabled_focus_mode = 2 39 | shortcut = null 40 | group = null 41 | text = "Hello" 42 | flat = false 43 | align = 1 44 | 45 | [node name="Label" type="Label" parent="." index="1"] 46 | anchor_left = 0.0 47 | anchor_top = 0.0 48 | anchor_right = 0.0 49 | anchor_bottom = 0.0 50 | margin_left = 392.0 51 | margin_top = 311.0 52 | margin_right = 629.0 53 | margin_bottom = 368.0 54 | rect_pivot_offset = Vector2( 0, 0 ) 55 | rect_clip_content = false 56 | mouse_filter = 2 57 | mouse_default_cursor_shape = 0 58 | size_flags_horizontal = 1 59 | size_flags_vertical = 0 60 | text = "ABC" 61 | percent_visible = 1.0 62 | lines_skipped = 0 63 | max_lines_visible = -1 64 | script = ExtResource( 2 ) 65 | _sections_unfolded = [ "Pause" ] 66 | 67 | [node name="Node" type="Node" parent="." index="2"] 68 | script = ExtResource( 3 ) 69 | _sections_unfolded = [ "Pause" ] 70 | node = null 71 | vector = Vector2( 2, 3 ) 72 | 73 | [node name="Node" type="Node" parent="Node" index="0"] 74 | script = ExtResource( 4 ) 75 | 76 | [connection signal="pressed" from="Button" to="." method="_on_Button_pressed"] 77 | -------------------------------------------------------------------------------- /example/Node.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | func _ready(): 4 | # Called every time the node is added to the scene. 5 | # Initialization here 6 | pass 7 | 8 | #func _process(delta): 9 | # # Called every frame. Delta is time since last frame. 10 | # # Update game logic here. 11 | # pass 12 | -------------------------------------------------------------------------------- /example/bin/godot-ruby.gdnlib: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | singleton=true 4 | load_once=true 5 | symbol_prefix="godot_" 6 | 7 | [entry] 8 | 9 | X11.64="res://bin/godot-ruby.so" 10 | Windows.64="res://bin/godot-ruby.dll" 11 | OSX.64="res://bin/godot-ruby.dylib" 12 | 13 | [dependencies] 14 | 15 | X11.64=[] 16 | Windows.64=[] 17 | OSX.64=[] 18 | -------------------------------------------------------------------------------- /example/bin/godot-ruby.so: -------------------------------------------------------------------------------- 1 | ../../bin/godot-ruby.so -------------------------------------------------------------------------------- /example/default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | radiance_size = 4 6 | sky_top_color = Color( 0.0470588, 0.454902, 0.976471, 1 ) 7 | sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 ) 8 | sky_curve = 0.25 9 | sky_energy = 1.0 10 | ground_bottom_color = Color( 0.101961, 0.145098, 0.188235, 1 ) 11 | ground_horizon_color = Color( 0.482353, 0.788235, 0.952941, 1 ) 12 | ground_curve = 0.01 13 | ground_energy = 1.0 14 | sun_color = Color( 1, 1, 1, 1 ) 15 | sun_latitude = 35.0 16 | sun_longitude = 0.0 17 | sun_angle_min = 1.0 18 | sun_angle_max = 100.0 19 | sun_curve = 0.05 20 | sun_energy = 16.0 21 | texture_size = 2 22 | 23 | [resource] 24 | 25 | background_mode = 2 26 | background_sky = SubResource( 1 ) 27 | background_sky_custom_fov = 0.0 28 | background_color = Color( 0, 0, 0, 1 ) 29 | background_energy = 1.0 30 | background_canvas_max_layer = 0 31 | ambient_light_color = Color( 0, 0, 0, 1 ) 32 | ambient_light_energy = 1.0 33 | ambient_light_sky_contribution = 1.0 34 | fog_enabled = false 35 | fog_color = Color( 0.5, 0.6, 0.7, 1 ) 36 | fog_sun_color = Color( 1, 0.9, 0.7, 1 ) 37 | fog_sun_amount = 0.0 38 | fog_depth_enabled = true 39 | fog_depth_begin = 10.0 40 | fog_depth_curve = 1.0 41 | fog_transmit_enabled = false 42 | fog_transmit_curve = 1.0 43 | fog_height_enabled = false 44 | fog_height_min = 0.0 45 | fog_height_max = 100.0 46 | fog_height_curve = 1.0 47 | tonemap_mode = 0 48 | tonemap_exposure = 1.0 49 | tonemap_white = 1.0 50 | auto_exposure_enabled = false 51 | auto_exposure_scale = 0.4 52 | auto_exposure_min_luma = 0.05 53 | auto_exposure_max_luma = 8.0 54 | auto_exposure_speed = 0.5 55 | ss_reflections_enabled = false 56 | ss_reflections_max_steps = 64 57 | ss_reflections_fade_in = 0.15 58 | ss_reflections_fade_out = 2.0 59 | ss_reflections_depth_tolerance = 0.2 60 | ss_reflections_roughness = true 61 | ssao_enabled = false 62 | ssao_radius = 1.0 63 | ssao_intensity = 1.0 64 | ssao_radius2 = 0.0 65 | ssao_intensity2 = 1.0 66 | ssao_bias = 0.01 67 | ssao_light_affect = 0.0 68 | ssao_color = Color( 0, 0, 0, 1 ) 69 | ssao_quality = 0 70 | ssao_blur = 3 71 | ssao_edge_sharpness = 4.0 72 | dof_blur_far_enabled = false 73 | dof_blur_far_distance = 10.0 74 | dof_blur_far_transition = 5.0 75 | dof_blur_far_amount = 0.1 76 | dof_blur_far_quality = 1 77 | dof_blur_near_enabled = false 78 | dof_blur_near_distance = 2.0 79 | dof_blur_near_transition = 1.0 80 | dof_blur_near_amount = 0.1 81 | dof_blur_near_quality = 1 82 | glow_enabled = false 83 | glow_levels/1 = false 84 | glow_levels/2 = false 85 | glow_levels/3 = true 86 | glow_levels/4 = false 87 | glow_levels/5 = true 88 | glow_levels/6 = false 89 | glow_levels/7 = false 90 | glow_intensity = 0.8 91 | glow_strength = 1.0 92 | glow_bloom = 0.0 93 | glow_blend_mode = 2 94 | glow_hdr_threshold = 1.0 95 | glow_hdr_scale = 2.0 96 | glow_bicubic_upscale = false 97 | adjustment_enabled = false 98 | adjustment_brightness = 1.0 99 | adjustment_contrast = 1.0 100 | adjustment_saturation = 1.0 101 | 102 | -------------------------------------------------------------------------------- /example/hello.rb: -------------------------------------------------------------------------------- 1 | extends :Object 2 | 3 | export :node, :NodePath 4 | export :vector, :Vector2, default: Vector2.new(2, 3) 5 | 6 | signal :click, [:a, :b] 7 | 8 | def _ready 9 | @aabb = Godot::Aabb.new(Godot::Vector3.new(3, 4, 5), Godot::Vector3.new(6, 7, 8)) 10 | @string = Godot::String.new("00000") 11 | @bunny_texture = ResourceLoader.load("res://icon.png") 12 | end 13 | 14 | def vector 15 | bunny = Sprite.new 16 | bunny.set(:texture, @bunny_texture) 17 | bunny.set(:position, Vector2[12, 12]) 18 | bunny 19 | end 20 | 21 | def multi a, b 22 | p a + b 23 | end 24 | -------------------------------------------------------------------------------- /example/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onyxblade/godot-ruby/8072844a24b6c178e06248d613ab68e78a28b3da/example/icon.png -------------------------------------------------------------------------------- /example/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | 7 | [deps] 8 | 9 | source_file="res://icon.png" 10 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 11 | 12 | [params] 13 | 14 | compress/mode=0 15 | compress/lossy_quality=0.7 16 | compress/hdr_mode=0 17 | compress/normal_map=0 18 | flags/repeat=0 19 | flags/filter=true 20 | flags/mipmaps=false 21 | flags/anisotropic=false 22 | flags/srgb=2 23 | process/fix_alpha_border=true 24 | process/premult_alpha=false 25 | process/HDR_as_SRGB=false 26 | stream=false 27 | size_limit=0 28 | detect_3d=true 29 | svg/scale=1.0 30 | -------------------------------------------------------------------------------- /example/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=3 10 | 11 | [application] 12 | 13 | config/name="New Game Project" 14 | run/main_scene="res://Main.tscn" 15 | config/icon="res://icon.png" 16 | 17 | [gdnative] 18 | 19 | singletons=[ "res://bin/godot-ruby.gdnlib" ] 20 | 21 | [rendering] 22 | 23 | environment/default_environment="res://default_env.tres" 24 | -------------------------------------------------------------------------------- /lib/godot.rb: -------------------------------------------------------------------------------- 1 | module Godot 2 | def self._register_class path, code 3 | @_classes ||= {} 4 | klass = ::Class.new(Godot::Object) 5 | klass.include Godot::AttachedScript 6 | klass.class_eval(code, path) 7 | @_classes[klass.object_id] = klass 8 | klass 9 | rescue Exception => e 10 | Godot._print_error e 11 | klass = ::Class.new(Godot::Object) 12 | klass.include Godot::AttachedScript 13 | klass 14 | end 15 | 16 | def self._unregister_class klass 17 | @_classes.delete(klass.object_id) 18 | end 19 | 20 | def self.call_method obj, name, *args 21 | obj.send(name, *args) 22 | rescue Exception => e 23 | Godot._print_error e 24 | nil 25 | end 26 | 27 | def self.built_in_type_class 28 | @_built_in_type_class ||= ::Class.new do 29 | class << self 30 | def _adopt addr 31 | obj = allocate 32 | obj.instance_variable_set(:@_godot_address, addr) 33 | ObjectSpace.define_finalizer(obj, finalizer_proc(addr)) 34 | obj 35 | end 36 | 37 | def finalizer_proc addr 38 | proc { self._finalize addr } 39 | end 40 | 41 | def const_missing name 42 | if Engine.has_singleton(name) 43 | Godot.const_set(name, Godot._get_singleton(name.to_s)) 44 | else 45 | Godot.const_set(name, Godot::Class.new(name)) 46 | end 47 | end 48 | 49 | def [] *args 50 | new *args 51 | end 52 | end 53 | 54 | end 55 | end 56 | 57 | def self._template_source_code base_name 58 | Godot::String.new <<~EOF 59 | extends :#{base_name} 60 | 61 | def _ready 62 | 63 | end 64 | EOF 65 | end 66 | 67 | def self._define_constants pool_string_array 68 | pool_string_array.size.times do |i| 69 | name = pool_string_array.get(i).to_s 70 | Godot.const_set(name, Godot._get_singleton(name)) 71 | end 72 | end 73 | 74 | def self._initialize_singletons 75 | singletons = ["ResourceLoader", "ResourceSaver", "OS", "Geometry", "ClassDB", "Engine", "AudioServer", "ProjectSettings", "Input", "InputMap", "Marshalls", "Performance", "Physics2DServer", "PhysicsServer", "TranslationServer", "VisualServer"] 76 | singletons.each do |name| 77 | Godot.const_set(name, Godot._get_singleton(name)) 78 | end 79 | ['TCPServer', 'File', 'Thread', 'Mutex', 'Range'].each do |name| 80 | Godot.const_set(name, Godot::Class.new(name)) 81 | end 82 | end 83 | 84 | def self._initialize_io 85 | io_class = ::Class.new(IO) do 86 | def write data 87 | str = data.to_s.chomp 88 | unless str.empty? 89 | Godot._print String.new(str) 90 | end 91 | end 92 | end 93 | 94 | godot_io = io_class.new(1) 95 | $stdout.reopen(godot_io) 96 | end 97 | 98 | def self._initialize_constants 99 | @_godot_constants = @_godot_constants.to_h.map do |name, int| 100 | [name.to_s, int] 101 | end.to_h 102 | @_godot_constants.each do |name, int| 103 | Godot.const_set(name, int) 104 | end 105 | end 106 | 107 | def self._initialize 108 | _initialize_singletons 109 | _initialize_io 110 | _initialize_constants 111 | rescue Exception => e 112 | Godot._print_error e 113 | end 114 | 115 | def self._register_object obj 116 | @_objects ||= {} 117 | @_objects[obj.object_id] = obj 118 | end 119 | 120 | def self._unregister_object obj 121 | @_objects.delete(obj.object_id) 122 | end 123 | 124 | def self._script_manifest klass 125 | klass.instance_eval do 126 | [ 127 | #name: 128 | String.new('anonymous'), 129 | #tool: 130 | @_tool ? 1 : 0, 131 | #base: 132 | String.new(@_base_name || 'Object'), 133 | #member_lines: 134 | Dictionary.new({}), 135 | #methods: 136 | Array.new(instance_methods(false).map{|m| 137 | { 138 | name: m, 139 | args: [], 140 | default_args: [], 141 | return: {}, 142 | flags: 1, 143 | rpc_mode: 0 144 | } 145 | }), 146 | #signals: 147 | Array.new(@_signals.to_a.map{|s| 148 | { 149 | name: s[:name], 150 | args: s[:args].map{|arg| 151 | { 152 | name: arg 153 | } 154 | }, 155 | default_args: [], 156 | return: {}, 157 | flags: 1, 158 | rpc_mode: 0 159 | } 160 | }), 161 | #properties: 162 | Array.new(@_exports.to_a.map{|p| 163 | { 164 | name: p[:name], 165 | type: p[:type], 166 | hint: p[:hint], 167 | hint_string: p[:hint_string], 168 | usage: 8199, 169 | default_value: p[:default_value], 170 | rset_mode: 0 171 | } 172 | }) 173 | ] 174 | end 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /lib/godot/attached_script.rb: -------------------------------------------------------------------------------- 1 | module Godot 2 | module AttachedScript 3 | module ClassMethods 4 | attr_reader :_exports, :_signals 5 | 6 | def extends name 7 | @_base_name = name.to_s 8 | end 9 | 10 | def export name, _type, **options 11 | case _type 12 | when :int 13 | type = TYPE_INT 14 | if options[:range] 15 | hint = PROPERTY_HINT_RANGE 16 | hint_string = [options[:range].min, options[:range].max, options[:step]].compact.join(',') 17 | elsif options[:enum] 18 | hint = PROPERTY_HINT_ENUM 19 | hint_string = options[:enum].join(',') 20 | end 21 | when :float 22 | type = TYPE_REAL 23 | if options[:range] 24 | hint = PROPERTY_HINT_RANGE 25 | hint_string = [options[:range].min, options[:range].max, options[:step]].compact.join(',') 26 | end 27 | when :String 28 | type = TYPE_STRING 29 | if options[:enum] 30 | hint = PROPERTY_HINT_ENUM 31 | hint_string = options[:enum].join(',') 32 | end 33 | when :exp 34 | type = TYPE_REAL 35 | hint = PROPERTY_HINT_EXP_RANGE 36 | hint_string = [options[:range].min, options[:range].max, options[:step]].compact.join(',') 37 | when :ease 38 | type = TYPE_REAL 39 | hint = PROPERTY_HINT_EXP_EASING 40 | when :file 41 | type = TYPE_STRING 42 | hint = PROPERTY_HINT_FILE 43 | if options[:extension] 44 | hint_string = options[:extension] 45 | end 46 | when :global_file 47 | type = TYPE_STRING 48 | hint = PROPERTY_HINT_GLOBAL_FILE 49 | if options[:extension] 50 | hint_string = options[:extension] 51 | end 52 | when :dir 53 | type = TYPE_STRING 54 | hint = PROPERTY_HINT_DIR 55 | if options[:extension] 56 | hint_string = options[:extension] 57 | end 58 | when :global_dir 59 | type = TYPE_STRING 60 | hint = PROPERTY_HINT_GLOBAL_DIR 61 | if options[:extension] 62 | hint_string = options[:extension] 63 | end 64 | when :multiline 65 | type = TYPE_STRING 66 | hint = PROPERTY_HINT_MULTILINE_TEXT 67 | when :rgb 68 | type = TYPE_COLOR 69 | hint = PROPERTY_HINT_COLOR_NO_ALPHA 70 | when :rgba 71 | type = TYPE_COLOR 72 | 73 | when :bool 74 | type = TYPE_BOOL 75 | when :Vector2 76 | type = TYPE_VECTOR2 77 | when :Rect2 78 | type = TYPE_RECT2 79 | when :Vector3 80 | type = TYPE_VECTOR3 81 | when :Transform2D 82 | type = TYPE_TRANSFORM2D 83 | when :Plane 84 | type = TYPE_PLANE 85 | when :Quat 86 | type = TYPE_QUAT 87 | when :Aabb 88 | type = TYPE_AABB 89 | when :Basis 90 | type = TYPE_BASIS 91 | when :Transform 92 | type = TYPE_TRANSFORM 93 | when :NodePath 94 | type = TYPE_NODE_PATH 95 | when :Rid 96 | type = TYPE_RID 97 | when :Dictionary 98 | type = TYPE_DICTIONARY 99 | when :Array 100 | type = TYPE_ARRAY 101 | when :PoolIntArray 102 | type = TYPE_INT_ARRAY 103 | when :PoolStringArray 104 | type = TYPE_STRING_ARRAY 105 | when :PoolVector2Array 106 | type = PoolVector2Array 107 | when :PoolVector3Array 108 | type = TYPE_VECTOR3_ARRAY 109 | when :PoolColorArray 110 | type = TYPE_COLOR_ARRAY 111 | 112 | else 113 | type = TYPE_OBJECT 114 | hint = PROPERTY_HINT_RESOURCE_TYPE 115 | end 116 | 117 | @_exports ||= [] 118 | @_exports << {name: name, type: type, hint: hint || PROPERTY_HINT_NONE, hint_string: hint_string || '', default_value: options[:default]} 119 | end 120 | 121 | def signal name, args = [] 122 | raise "signal arguments should be an array" if !args.is_a?(::Array) 123 | @_signals ||= [] 124 | @_signals << { 125 | name: name, 126 | args: args 127 | } 128 | end 129 | 130 | def tool 131 | @_tool = true 132 | end 133 | end 134 | 135 | module InstanceMethods 136 | def initialize 137 | Godot._register_object self 138 | end 139 | end 140 | 141 | def self.included base 142 | base.extend ClassMethods 143 | base.include InstanceMethods 144 | end 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /lib/godot/class.rb: -------------------------------------------------------------------------------- 1 | module Godot 2 | class Class 3 | def initialize name 4 | @name = name 5 | end 6 | 7 | def new 8 | ClassDB.instance(@name) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/godot/object.rb: -------------------------------------------------------------------------------- 1 | module Godot 2 | class Object < Godot.built_in_type_class 3 | def method_missing name, *args, &block 4 | if respond_to? name 5 | puts "super to Godot::Object will lead to endless loop" 6 | else 7 | _call name, args 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ruby util/generate.rb 3 | gcc -std=c11 -fPIC -c src/godot-ruby.c -I/home/cichol/godot_headers/ -I/home/cichol/.rbenv/versions/2.5.1/include/ruby-2.5.0/x86_64-linux -I/home/cichol/.rbenv/versions/2.5.1/include/ruby-2.5.0 -o src/godot-ruby.os -Wno-discarded-qualifiers 4 | gcc -shared src/godot-ruby.os -o bin/godot-ruby.so -L/home/cichol/.rbenv/versions/2.5.1/lib -lruby 5 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | void print_godot_string(const godot_string *str) { 2 | printf("%ls\n", api->godot_string_wide_str(str)); 3 | } 4 | 5 | void print_godot_string_name(const godot_string_name *strname) { 6 | godot_string str = api->godot_string_name_get_name(strname); 7 | print_godot_string(&str); 8 | api->godot_string_destroy(&str); 9 | } 10 | 11 | void ruby_p(const VALUE value) { 12 | rb_funcall(rb_cObject, rb_intern("p"), 1, value); 13 | } 14 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | void print_godot_string(const godot_string *str); 2 | void print_godot_string_name(const godot_string_name *strname); 3 | void ruby_p(const VALUE value); 4 | -------------------------------------------------------------------------------- /src/godot-ruby.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "debug.h" 7 | #include "generated.c" 8 | #include "pluginscript.c" 9 | #include "debug.c" 10 | -------------------------------------------------------------------------------- /src/pluginscript.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const godot_gdnative_core_api_struct *api = NULL; 4 | const godot_gdnative_ext_pluginscript_api_struct *pluginscript_api = NULL; 5 | 6 | static const char *RUBY_RECOGNIZED_EXTENSIONS[] = { "rb", 0 }; 7 | static const char *RUBY_RESERVED_WORDS[] = { 8 | "__ENCODING__", 9 | "__LINE__", 10 | "__FILE__", 11 | "BEGIN", 12 | "END", 13 | "alias", 14 | "and", 15 | "begin", 16 | "break", 17 | "case", 18 | "class", 19 | "def", 20 | "defined?", 21 | "do", 22 | "else", 23 | "elsif", 24 | "end", 25 | "ensure", 26 | "false", 27 | "for", 28 | "if", 29 | "in", 30 | "module", 31 | "next", 32 | "nil", 33 | "not", 34 | "or", 35 | "redo", 36 | "rescue", 37 | "retry", 38 | "return", 39 | "self", 40 | "super", 41 | "then", 42 | "true", 43 | "undef", 44 | "unless", 45 | "until", 46 | "when", 47 | "while", 48 | "yield", 49 | 0 50 | }; 51 | static const char *RUBY_COMMENT_DELIMITERS[] = { "#", 0 }; 52 | static const char *RUBY_STRING_DELIMITERS[] = { "\" \"", "' '", 0 }; 53 | static godot_pluginscript_language_desc desc; 54 | 55 | static VALUE rb_mGodot; 56 | static pthread_t ruby_thread_id; 57 | 58 | typedef struct { 59 | VALUE klass; 60 | } godot_ruby_pluginscript_script_data; 61 | 62 | typedef struct { 63 | VALUE object; 64 | godot_object *owner; 65 | } godot_ruby_pluginscript_instance_data; 66 | 67 | godot_string godot_ruby_get_template_source_code(godot_pluginscript_language_data *p_data, const godot_string *p_class_name, const godot_string *p_base_class_name) { 68 | // p_class_name is the filename 69 | VALUE template = rb_funcall(rb_mGodot, rb_intern("_template_source_code"), 1, rb_godot_string_pointer_from_godot(p_base_class_name)); 70 | godot_string ret; 71 | api->godot_string_new_copy(&ret, rb_godot_string_pointer_to_godot(template)); 72 | printf("get_template_source_code\n"); 73 | return ret; 74 | } 75 | void godot_ruby_add_global_constant(godot_pluginscript_language_data *p_data, const godot_string *p_variable, const godot_variant *p_value) { 76 | printf("add_global_constant\n"); 77 | } 78 | 79 | VALUE rb_godot_object_call(VALUE self, VALUE method_name, VALUE method_args) { 80 | godot_object *pointer = rb_godot_object_pointer_to_godot(self); 81 | godot_variant gv_args = rb_godot_variant_to_godot(method_args); 82 | godot_variant gv_name = rb_godot_variant_to_godot(method_name); 83 | 84 | godot_variant_call_error p_error; 85 | godot_method_bind *method_bind = api->godot_method_bind_get_method("Object", "callv"); 86 | 87 | const godot_variant *c_args[] = { 88 | &gv_name, 89 | &gv_args 90 | }; 91 | godot_variant ret = api->godot_method_bind_call(method_bind, pointer, c_args, 2, &p_error); 92 | // printf("call error %d", p_error.error); 93 | return rb_godot_variant_from_godot(ret); 94 | } 95 | 96 | VALUE rb_godot_get_global_singleton (VALUE self, VALUE name) { 97 | godot_object* klass = api->godot_global_get_singleton(StringValueCStr(name)); 98 | 99 | if (klass) { 100 | return rb_godot_object_pointer_from_godot(klass); 101 | } else { 102 | return Qnil; 103 | } 104 | } 105 | 106 | VALUE rb_godot_print (VALUE self, VALUE string) { 107 | api->godot_print(rb_godot_string_pointer_to_godot(string)); 108 | return Qtrue; 109 | } 110 | 111 | VALUE rb_godot_print_error (VALUE self, VALUE exception) { 112 | VALUE message = rb_funcall(exception, rb_intern("message"), 0); 113 | VALUE backtrace = rb_funcall(rb_funcall(exception, rb_intern("backtrace"), 0), rb_intern("to_s"), 0); 114 | api->godot_print_error(StringValueCStr(message), StringValueCStr(backtrace), "ruby", 0); 115 | return Qtrue; 116 | } 117 | 118 | godot_pluginscript_language_data *godot_ruby_init() { 119 | printf("godot_ruby_init\n"); 120 | ruby_init(); 121 | ruby_script("godot"); 122 | ruby_init_loadpath(); 123 | // VALUE load_path = rb_gv_get("$LOAD_PATH"); 124 | // rb_funcall(load_path, rb_intern("unshift"), 1, rb_str_new_cstr("/home/cichol/godot-ruby/lib")); 125 | 126 | rb_eval_string(RUBY_CODE); 127 | 128 | rb_mGodot = rb_const_get(rb_cModule, rb_intern("Godot")); 129 | 130 | VALUE object_module = rb_const_get(rb_mGodot, rb_intern("Object")); 131 | rb_define_method(object_module, "_call", &rb_godot_object_call, 2); 132 | 133 | rb_define_singleton_method(rb_mGodot, "_get_singleton", &rb_godot_get_global_singleton, 1); 134 | rb_define_singleton_method(rb_mGodot, "_print", &rb_godot_print, 1); 135 | rb_define_singleton_method(rb_mGodot, "_print_error", &rb_godot_print_error, 1); 136 | 137 | init(); 138 | 139 | godot_dictionary constant_dict = api->godot_get_global_constants(); 140 | rb_iv_set(rb_mGodot, "@_godot_constants", rb_godot_dictionary_from_godot(constant_dict)); 141 | 142 | rb_funcall(rb_mGodot, rb_intern("_initialize"), 0); 143 | 144 | ruby_thread_id = pthread_self(); 145 | return NULL; 146 | } 147 | void godot_ruby_finish(godot_pluginscript_language_data *p_data) { 148 | printf("ruby_finish\n"); 149 | ruby_cleanup(0); 150 | } 151 | 152 | godot_pluginscript_script_manifest godot_ruby_script_init(godot_pluginscript_language_data *p_data, const godot_string *p_path, const godot_string *p_source, godot_error *r_error) { 153 | godot_pluginscript_script_manifest manifest; 154 | 155 | if (ruby_thread_id != pthread_self()) { 156 | printf("script_init called from another thread\n"); 157 | *r_error = GODOT_ERR_LOCKED; 158 | return manifest; 159 | } 160 | 161 | VALUE r_path = rb_godot_string_pointer_from_godot(p_path); 162 | VALUE r_source = rb_godot_string_pointer_from_godot(p_source); 163 | 164 | VALUE klass = rb_funcall(rb_mGodot, rb_intern("_register_class"), 2, rb_funcall(r_path, rb_intern("to_s"), 0), rb_funcall(r_source, rb_intern("to_s"), 0)); 165 | 166 | godot_ruby_pluginscript_script_data *data; 167 | data = (godot_ruby_pluginscript_script_data*)api->godot_alloc(sizeof(godot_ruby_pluginscript_script_data)); 168 | data->klass = klass; 169 | 170 | VALUE script_manifest = rb_funcall(rb_mGodot, rb_intern("_script_manifest"), 1, klass); 171 | 172 | godot_string_name name; 173 | api->godot_string_name_new(&name, rb_godot_string_pointer_to_godot(RARRAY_AREF(script_manifest, 0))); 174 | 175 | godot_int is_tool = rb_godot_int_to_godot(RARRAY_AREF(script_manifest, 1)); 176 | 177 | godot_string_name base; 178 | api->godot_string_name_new(&base, rb_godot_string_pointer_to_godot(RARRAY_AREF(script_manifest, 2))); 179 | 180 | godot_dictionary member_lines; 181 | api->godot_dictionary_new_copy(&member_lines, rb_godot_dictionary_pointer_to_godot(RARRAY_AREF(script_manifest, 3))); 182 | 183 | godot_array methods; 184 | api->godot_array_new_copy(&methods, rb_godot_array_pointer_to_godot(RARRAY_AREF(script_manifest, 4))); 185 | 186 | godot_array signals; 187 | api->godot_array_new_copy(&signals, rb_godot_array_pointer_to_godot(RARRAY_AREF(script_manifest, 5))); 188 | 189 | godot_array properties; 190 | api->godot_array_new_copy(&properties, rb_godot_array_pointer_to_godot(RARRAY_AREF(script_manifest, 6))); 191 | 192 | manifest.data = (godot_pluginscript_script_data*) data; 193 | manifest.name = name; 194 | manifest.is_tool = is_tool; 195 | manifest.base = base; 196 | manifest.member_lines = member_lines; 197 | manifest.methods = methods; 198 | manifest.signals = signals; 199 | manifest.properties = properties; 200 | 201 | *r_error = GODOT_OK; 202 | printf("ruby_script_init\n"); 203 | return manifest; 204 | } 205 | void godot_ruby_script_finish(godot_pluginscript_script_data *p_data) { 206 | godot_ruby_pluginscript_script_data *data = (godot_ruby_pluginscript_script_data*) p_data; 207 | api->godot_free(p_data); 208 | 209 | if (ruby_thread_id != pthread_self()) { 210 | printf("script_init called from another thread\n"); 211 | return; 212 | } 213 | rb_funcall(rb_mGodot, rb_intern("_unregister_class"), 1, data->klass); 214 | printf("script_finish\n"); 215 | } 216 | 217 | godot_pluginscript_instance_data *godot_ruby_instance_init(godot_pluginscript_script_data *p_data, godot_object *p_owner) { 218 | godot_ruby_pluginscript_script_data *script_data = (godot_ruby_pluginscript_script_data*) p_data; 219 | godot_ruby_pluginscript_instance_data *data; 220 | data = (godot_ruby_pluginscript_instance_data*)api->godot_alloc(sizeof(godot_ruby_pluginscript_instance_data)); 221 | 222 | VALUE instance = rb_funcall(script_data->klass, rb_intern("new"), 0); 223 | 224 | data->object = instance; 225 | data->owner = p_owner; 226 | rb_iv_set(instance, "@_godot_address", LONG2NUM((long)p_owner)); 227 | 228 | printf("ruby_instance_init\n"); 229 | return (godot_pluginscript_instance_data*)data; 230 | } 231 | 232 | void godot_ruby_instance_finish(godot_pluginscript_instance_data *p_data) { 233 | godot_ruby_pluginscript_instance_data *data = (godot_ruby_pluginscript_instance_data *) p_data; 234 | rb_funcall(rb_mGodot, rb_intern("_unregister_object"), 1, data->object); 235 | api->godot_free(p_data); 236 | printf("instance_finish\n"); 237 | } 238 | 239 | godot_bool godot_ruby_instance_set_prop(godot_pluginscript_instance_data *p_data, const godot_string *p_name, const godot_variant *p_value) { 240 | godot_ruby_pluginscript_instance_data *data = (godot_ruby_pluginscript_instance_data*) p_data; 241 | printf("instance_set_prop\n"); 242 | VALUE method_name = rb_funcall(rb_funcall(rb_godot_string_pointer_from_godot(p_name), rb_intern("to_s"), 0), rb_intern("concat"), 1, rb_str_new_cstr("=")); 243 | 244 | if (RTEST(rb_funcall(data->object, rb_intern("respond_to?"), 1, method_name))) { 245 | rb_funcall(data->object, rb_intern_str(method_name), 1, rb_godot_variant_from_godot(*p_value)); 246 | return 1; 247 | } else { 248 | return 0; 249 | } 250 | } 251 | godot_bool godot_ruby_instance_get_prop(godot_pluginscript_instance_data *p_data, const godot_string *p_name, godot_variant *r_ret) { 252 | godot_ruby_pluginscript_instance_data *data = (godot_ruby_pluginscript_instance_data*) p_data; 253 | printf("instance_get_prop\n"); 254 | VALUE method_name = rb_funcall(rb_godot_string_pointer_from_godot(p_name), rb_intern("to_s"), 0); 255 | 256 | if (RTEST(rb_funcall(data->object, rb_intern("respond_to?"), 1, method_name))) { 257 | godot_variant var; 258 | VALUE ret = rb_funcall(data->object, rb_intern_str(method_name), 0); 259 | var = rb_godot_variant_to_godot(ret); 260 | memcpy(r_ret, &var, sizeof(godot_variant)); 261 | return 1; 262 | } else { 263 | return 0; 264 | } 265 | } 266 | 267 | godot_variant godot_ruby_instance_call_method(godot_pluginscript_instance_data *p_data, const godot_string_name *p_method, const godot_variant **p_args, int p_argcount, godot_variant_call_error *r_error) { 268 | printf("instance_call_method\n"); 269 | godot_ruby_pluginscript_instance_data *data = (godot_ruby_pluginscript_instance_data*) p_data; 270 | 271 | godot_string method_name = api->godot_string_name_get_name(p_method); 272 | VALUE method_name_str = rb_funcall(rb_godot_string_pointer_from_godot(&method_name), rb_intern("to_s"), 0); 273 | VALUE respond_to = rb_funcall(data->object, rb_intern("respond_to?"), 1, rb_funcall(method_name_str, rb_intern("to_s"), 0)); 274 | godot_variant var; 275 | 276 | if (RTEST(respond_to)) { 277 | VALUE arguments[p_argcount+2]; 278 | arguments[0] = data->object; 279 | arguments[1] = method_name_str; 280 | 281 | for (int i=0; i < p_argcount; ++i) { 282 | arguments[i + 2] = rb_godot_variant_from_godot(*p_args[i]); 283 | } 284 | 285 | VALUE ret = rb_funcallv(rb_mGodot, rb_intern("call_method"), p_argcount + 2, arguments); 286 | 287 | var = rb_godot_variant_to_godot(ret); 288 | } else { 289 | VALUE klass = rb_funcall(data->object, rb_intern("class"), 0); 290 | VALUE base_name = rb_iv_get(klass, "@_base_name"); 291 | 292 | godot_method_bind *method; 293 | wchar_t *wchars = api->godot_string_wide_str(&method_name); 294 | { 295 | int len = api->godot_string_length(&method_name); 296 | char chars[len+1]; 297 | wcstombs(chars, wchars, len + 1); 298 | 299 | method = api->godot_method_bind_get_method(StringValueCStr(base_name), chars); 300 | 301 | if (method) { 302 | var = api->godot_method_bind_call(method, data->owner, p_args, p_argcount, r_error); 303 | } else { 304 | api->godot_variant_new_nil(&var); 305 | r_error->error = GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD; 306 | } 307 | } 308 | } 309 | 310 | api->godot_string_destroy(&method_name); 311 | return var; 312 | } 313 | 314 | void godot_ruby_instance_notification(godot_pluginscript_instance_data *p_data, int p_notification) { 315 | printf("instance_notification\n"); 316 | } 317 | 318 | godot_bool godot_ruby_validate(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_pool_string_array *r_functions) { 319 | printf("validate\n"); 320 | } 321 | 322 | int godot_ruby_find_function(godot_pluginscript_language_data *p_data, const godot_string *p_function, const godot_string *p_code) { 323 | printf("find_function\n"); 324 | } 325 | godot_string godot_ruby_make_function(godot_pluginscript_language_data *p_data, const godot_string *p_class, const godot_string *p_name, const godot_pool_string_array *p_args) { 326 | printf("make_function\n"); 327 | } 328 | godot_error godot_ruby_complete_code(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_base_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint) { 329 | printf("complete_code\n"); 330 | 331 | } 332 | void godot_ruby_auto_indent_code(godot_pluginscript_language_data *p_data, godot_string *p_code, int p_from_line, int p_to_line) { 333 | printf("auto_indent_code\n"); 334 | } 335 | godot_string godot_ruby_debug_get_error(godot_pluginscript_language_data *p_data) { 336 | printf("debug_get_error\n"); 337 | } 338 | int godot_ruby_debug_get_stack_level_count(godot_pluginscript_language_data *p_data) { 339 | printf("debug_get_stack_level_count\n"); 340 | } 341 | int godot_ruby_debug_get_stack_level_line(godot_pluginscript_language_data *p_data, int p_level) { 342 | printf("debug_get_stack_level_line\n"); 343 | } 344 | godot_string godot_ruby_debug_get_stack_level_function(godot_pluginscript_language_data *p_data, int p_level) { 345 | printf("debug_get_stack_level_function\n"); 346 | } 347 | godot_string godot_ruby_debug_get_stack_level_source(godot_pluginscript_language_data *p_data, int p_level) { 348 | printf("debug_get_stack_level_source\n"); 349 | } 350 | void godot_ruby_debug_get_stack_level_locals(godot_pluginscript_language_data *p_data, int p_level, godot_pool_string_array *p_locals, godot_array *p_values, int p_max_subitems, int p_max_depth) { 351 | printf("debug_get_stack_level_locals\n"); 352 | } 353 | void godot_ruby_debug_get_stack_level_members(godot_pluginscript_language_data *p_data, int p_level, godot_pool_string_array *p_members, godot_array *p_values, int p_max_subitems, int p_max_depth) { 354 | printf("debug_get_stack_level_members\n"); 355 | } 356 | void godot_ruby_debug_get_globals(godot_pluginscript_language_data *p_data, godot_pool_string_array *p_locals, godot_array *p_values, int p_max_subitems, int p_max_depth) { 357 | printf("debug_get_globals\n"); 358 | } 359 | godot_string godot_ruby_debug_parse_stack_level_expression(godot_pluginscript_language_data *p_data, int p_level, const godot_string *p_expression, int p_max_subitems, int p_max_depth) { 360 | printf("debug_parse_stack_level_expression\n"); 361 | } 362 | void godot_ruby_profiling_start(godot_pluginscript_language_data *p_data) { 363 | printf("profiling_start\n"); 364 | } 365 | void godot_ruby_profiling_stop(godot_pluginscript_language_data *p_data) { 366 | printf("profiling_stop\n"); 367 | } 368 | int godot_ruby_profiling_get_accumulated_data(godot_pluginscript_language_data *p_data, godot_pluginscript_profiling_data *r_info, int p_info_max) { 369 | printf("godot_ruby_profiling_get_accumulated_data\n"); 370 | } 371 | int godot_ruby_profiling_get_frame_data(godot_pluginscript_language_data *p_data, godot_pluginscript_profiling_data *r_info, int p_info_max) { 372 | printf("godot_ruby_profiling_get_frame_data\n"); 373 | } 374 | void godot_ruby_profiling_frame(godot_pluginscript_language_data *p_data) { 375 | printf("profiling_frame\n"); 376 | } 377 | 378 | void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *p_options) { 379 | printf("gdnative init\n"); 380 | 381 | api = p_options->api_struct; 382 | 383 | // now find our extensions 384 | for (int i = 0; i < api->num_extensions; i++) { 385 | switch (api->extensions[i]->type) { 386 | case GDNATIVE_EXT_PLUGINSCRIPT: 387 | pluginscript_api = (godot_gdnative_ext_pluginscript_api_struct *)api->extensions[i]; 388 | break; 389 | default: break; 390 | }; 391 | }; 392 | 393 | desc.name = "Ruby"; 394 | desc.type = "Ruby"; 395 | desc.extension = "rb"; 396 | desc.recognized_extensions = RUBY_RECOGNIZED_EXTENSIONS; 397 | desc.init = &godot_ruby_init; 398 | desc.finish = &godot_ruby_finish; 399 | desc.reserved_words = RUBY_RESERVED_WORDS; 400 | desc.comment_delimiters = RUBY_COMMENT_DELIMITERS; 401 | desc.string_delimiters = RUBY_STRING_DELIMITERS; 402 | desc.has_named_classes = false; 403 | desc.get_template_source_code = &godot_ruby_get_template_source_code; 404 | desc.add_global_constant = &godot_ruby_add_global_constant; 405 | 406 | desc.script_desc.init = &godot_ruby_script_init; 407 | desc.script_desc.finish = &godot_ruby_script_finish; 408 | 409 | desc.script_desc.instance_desc.init = &godot_ruby_instance_init; 410 | desc.script_desc.instance_desc.finish = &godot_ruby_instance_finish; 411 | desc.script_desc.instance_desc.set_prop = &godot_ruby_instance_set_prop; 412 | desc.script_desc.instance_desc.get_prop = &godot_ruby_instance_get_prop; 413 | desc.script_desc.instance_desc.call_method = &godot_ruby_instance_call_method; 414 | desc.script_desc.instance_desc.notification = &godot_ruby_instance_notification; 415 | desc.script_desc.instance_desc.refcount_incremented = NULL; 416 | desc.script_desc.instance_desc.refcount_decremented = NULL; 417 | 418 | if (p_options->in_editor) { 419 | desc.get_template_source_code = &godot_ruby_get_template_source_code; 420 | /* 421 | desc.validate = &godot_ruby_validate; 422 | desc.find_function = &godot_ruby_find_function; 423 | desc.make_function = &godot_ruby_make_function; 424 | desc.complete_code = &godot_ruby_complete_code; 425 | desc.auto_indent_code = &godot_ruby_auto_indent_code; 426 | 427 | desc.debug_get_error = &godot_ruby_debug_get_error; 428 | desc.debug_get_stack_level_count = &godot_ruby_debug_get_stack_level_count; 429 | desc.debug_get_stack_level_line = &godot_ruby_debug_get_stack_level_line; 430 | desc.debug_get_stack_level_function = &godot_ruby_debug_get_stack_level_function; 431 | desc.debug_get_stack_level_source = &godot_ruby_debug_get_stack_level_source; 432 | desc.debug_get_stack_level_locals = &godot_ruby_debug_get_stack_level_locals; 433 | desc.debug_get_stack_level_members = &godot_ruby_debug_get_stack_level_members; 434 | desc.debug_get_globals = &godot_ruby_debug_get_globals; 435 | desc.debug_parse_stack_level_expression = &godot_ruby_debug_parse_stack_level_expression; 436 | 437 | desc.profiling_start = &godot_ruby_profiling_start; 438 | desc.profiling_stop = &godot_ruby_profiling_stop; 439 | desc.profiling_get_accumulated_data = &godot_ruby_profiling_get_accumulated_data; 440 | desc.profiling_get_frame_data = &godot_ruby_profiling_get_frame_data; 441 | desc.profiling_frame = &godot_ruby_profiling_frame; 442 | */ 443 | } 444 | pluginscript_api->godot_pluginscript_register_language(&desc); 445 | printf("registered language\n"); 446 | } 447 | 448 | void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *p_options) { 449 | printf("gdnative_terminate\n"); 450 | api = NULL; 451 | pluginscript_api = NULL; 452 | } 453 | 454 | void GDN_EXPORT godot_gdnative_singleton() { 455 | } 456 | 457 | -------------------------------------------------------------------------------- /util/generate.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | require_relative 'generator' 4 | require_relative 'generator/type' 5 | require_relative 'generator/type/base' 6 | require_relative 'generator/type/struct' 7 | require_relative 'generator/type/stack' 8 | require_relative 'generator/type/stack_pointer' 9 | require_relative 'generator/type/heap' 10 | require_relative 'generator/type/heap_pointer' 11 | require_relative 'generator/type/alias' 12 | require_relative 'generator/class' 13 | require_relative 'generator/class/base' 14 | require_relative 'generator/class/struct' 15 | require_relative 'generator/class/stack' 16 | require_relative 'generator/class/heap' 17 | require_relative 'generator/function' 18 | require_relative 'generator/function/argument' 19 | 20 | Dir.glob("#{__dir__}/generator/classes/*.rb").each do |file| 21 | require file 22 | end 23 | Dir.glob("#{__dir__}/generator/types/*.rb").each do |file| 24 | require file 25 | end 26 | require_relative 'generator/classes' 27 | require_relative 'generator/types' 28 | 29 | #puts Godot::Generator::Class.generate_class_static_definitions 30 | #puts Godot::Generator::Type.generate_godot_convert_functions 31 | #puts Godot::Generator::Class.generate_class_initialization_statements 32 | #puts Godot::Generator::Class.generate_class_initializer_functions 33 | #puts Godot::Generator::Class.generate_class_finalizer_functions 34 | #puts Godot::Generator::Class.get_class(:Array).api_functions 35 | 36 | #puts Godot::Generator::Class.get_class(:NodePath).api_functions 37 | #puts Godot::Generator::Class.get_class(:NodePath).variant_branch 38 | 39 | Godot::Generator.generate_c_functions 40 | -------------------------------------------------------------------------------- /util/generator.rb: -------------------------------------------------------------------------------- 1 | module Godot 2 | module Generator 3 | def self.generate_c_functions 4 | File.open("#{__dir__}/../src/generated.c", 'w'){|f| 5 | f.write "extern const godot_gdnative_core_api_struct *api;\n" 6 | f.write Godot::Generator::Type.generate_godot_convert_function_headers.join("\n") 7 | f.write Godot::Generator::Class.generate_class_static_definitions.join 8 | f.write Godot::Generator::Type.generate_godot_convert_functions.join 9 | f.write Godot::Generator::Class.generate_class_initializer_functions.join 10 | f.write Godot::Generator::Class.generate_class_finalizer_functions.join 11 | f.write Godot::Generator::Class.generate_class_instance_functions.join 12 | f.write Godot::Generator::Class.generate_class_type_functions.join 13 | f.write <<~EOF 14 | const char *RUBY_CODE = "#{generate_ruby_code.gsub("\n", "\\\\n\\\n").gsub('"', '\"')}"; 15 | 16 | void init() { 17 | #{Godot::Generator::Class.generate_class_initialization_statements} 18 | #{Godot::Generator::Class.generate_class_register_method_statements.join("\n")} 19 | } 20 | EOF 21 | } 22 | end 23 | 24 | def self.generate_ruby_code 25 | [ 26 | File.open("#{__dir__}/../lib/godot.rb", &:read), 27 | File.open("#{__dir__}/../lib/godot/object.rb", &:read), 28 | File.open("#{__dir__}/../lib/godot/class.rb", &:read), 29 | File.open("#{__dir__}/../lib/godot/attached_script.rb", &:read), 30 | Godot::Generator::Class.generate_class_ruby_definitions.join 31 | ].join 32 | end 33 | end 34 | end 35 | 36 | -------------------------------------------------------------------------------- /util/generator/class.rb: -------------------------------------------------------------------------------- 1 | 2 | module Godot::Generator 3 | module Class 4 | class << self 5 | attr_reader :classes 6 | 7 | def register_class klass 8 | @classes ||= {} 9 | @classes[klass.name.to_s] = klass 10 | end 11 | 12 | def get_class name 13 | klass = classes[name.to_s] 14 | raise "unknown class #{name}" unless klass 15 | klass 16 | end 17 | 18 | def generate_class_static_definitions 19 | classes.values.map do |c| 20 | "static VALUE #{c.name}_class;\n" 21 | end 22 | end 23 | 24 | def generate_class_initialization_statements 25 | <<~EOF 26 | VALUE godot_module = rb_const_get(rb_cModule, rb_intern("Godot")); 27 | #{ 28 | classes.values.map do |c| 29 | "#{c.name}_class = rb_const_get(godot_module, rb_intern(\"#{c.name}\"));" 30 | end.join("\n") 31 | } 32 | EOF 33 | end 34 | 35 | def generate_class_initializer_functions 36 | classes.values.map do |c| 37 | c.initializer_functions 38 | end 39 | end 40 | 41 | def generate_class_finalizer_functions 42 | classes.values.map do |c| 43 | c.finalizer_function 44 | end 45 | end 46 | 47 | def generate_class_instance_functions 48 | classes.values.map do |c| 49 | c.instance_functions 50 | end 51 | end 52 | 53 | def generate_class_register_method_statements 54 | classes.values.map do |c| 55 | [ 56 | c.register_method_statements, 57 | c.register_operator_statements 58 | ] 59 | end.flatten 60 | end 61 | 62 | def generate_class_ruby_definitions 63 | classes.values.map do |c| 64 | c.class_definition 65 | end 66 | end 67 | 68 | def generate_class_type_functions 69 | classes.values.map do |c| 70 | c.type_function 71 | end 72 | end 73 | 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /util/generator/class/base.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class Base 4 | def name 5 | self.class.name.split('::').last 6 | end 7 | 8 | def type_name 9 | "godot_#{name.downcase}" 10 | end 11 | 12 | def type_id 13 | self.class.const_get(:ID) 14 | end 15 | 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /util/generator/class/heap.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class Heap < Struct 4 | def initializer_function initializer = nil 5 | <<~EOF 6 | VALUE rb_#{type_name}_initialize(VALUE self, VALUE value){ 7 | #{type_name} *addr = api->godot_alloc(sizeof(#{type_name})); 8 | #{initializer} 9 | return rb_iv_set(self, "@_godot_address", LONG2NUM((long)addr)); 10 | } 11 | EOF 12 | end 13 | 14 | def finalizer_function 15 | <<~EOF 16 | VALUE rb_#{type_name}_finalize (VALUE self, VALUE addr) { 17 | api->#{type_name}_destroy((#{type_name}*)NUM2LONG(addr)); 18 | api->godot_free((void*)NUM2LONG(addr)); 19 | return Qtrue; 20 | } 21 | EOF 22 | end 23 | 24 | def initialize_method 25 | statement = constructor.arguments.first.type.source_classes.map{|klass| "arg.is_a?(#{klass})"}.join(" || ") 26 | <<~EOF 27 | def initialize arg 28 | if #{statement} 29 | _initialize(arg) 30 | else 31 | raise "mismatched arguments" 32 | end 33 | ObjectSpace.define_finalizer(self, self.class.finalizer_proc(@_godot_address)) 34 | end 35 | EOF 36 | end 37 | 38 | def constructor 39 | api_functions.find{|x| x.name == "#{type_name}_new"} 40 | end 41 | 42 | def register_method_statements 43 | initializer = "rb_define_method(#{name}_class, \"_initialize\", &rb_#{type_name}_initialize, 1);" 44 | methods = instance_methods.map do |func| 45 | method_name = "#{func.name.gsub("#{type_name}_", '')}" 46 | "rb_define_method(#{name}_class, \"#{method_name}\", &rb_#{type_name}_#{method_name}, #{func.arguments.size - 1});" 47 | end 48 | finalizer = "rb_define_singleton_method(#{name}_class, \"_finalize\", &rb_#{type_name}_finalize, 0);" 49 | type_func = "rb_define_method(#{name}_class, \"_type\", &rb_#{type_name}_type, 0);" 50 | [initializer, methods, finalizer, type_func].flatten 51 | end 52 | 53 | def class_definition 54 | <<~EOF 55 | module Godot 56 | class #{name} < Godot.built_in_type_class 57 | #{initialize_method} 58 | end 59 | end 60 | EOF 61 | end 62 | 63 | def initializer_functions 64 | [initializer_function] 65 | end 66 | 67 | def functions 68 | [initializer_function, to_godot_function, from_godot_function, finalizer_function].flatten.join("\n") 69 | end 70 | 71 | #def instance_functions 72 | #end 73 | 74 | def to_godot_call name 75 | "rb_godot_#{c_name}_to_godot(#{name})" 76 | end 77 | 78 | def from_godot_call name 79 | "rb_godot_#{c_name}_from_godot(#{name})" 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /util/generator/class/stack.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class Stack < Struct 4 | attr_reader :name 5 | 6 | def initialize name 7 | @name = name 8 | end 9 | 10 | def initializer_functions 11 | constructors.map do |func| 12 | params = func.arguments_without_self.map{|arg| "VALUE #{arg.name}"}.join(', ') 13 | args = func.arguments_without_self.map{|arg| arg.type.to_godot arg.name}.join(', ') 14 | 15 | function_name = func.name.gsub('new', 'initialize') 16 | <<~EOF 17 | VALUE rb_#{function_name}(VALUE self, #{params}){ 18 | #{type_name} *addr = api->godot_alloc(sizeof(#{type_name})); 19 | api->#{func.name}(addr, #{args}); 20 | return rb_iv_set(self, "@_godot_address", LONG2NUM((long)addr)); 21 | } 22 | EOF 23 | end.join("\n") 24 | end 25 | 26 | def finalizer_function 27 | <<~EOF 28 | VALUE rb_#{type_name}_finalize (VALUE self, VALUE addr) { 29 | api->godot_free((void*)NUM2LONG(addr)); 30 | return Qtrue; 31 | } 32 | EOF 33 | end 34 | 35 | def initialize_method 36 | dispatcher_method :initialize, constructors, <<~EOF 37 | ObjectSpace.define_finalizer(self, self.class.finalizer_proc(@_godot_address)) 38 | EOF 39 | end 40 | 41 | def class_definition 42 | <<~EOF 43 | module Godot 44 | class #{name} < Godot.built_in_type_class 45 | #{initialize_method} 46 | #{operator_methods} 47 | end 48 | end 49 | EOF 50 | end 51 | 52 | def register_method_statements 53 | initializers = constructors.map do |func| 54 | initializer_name = "#{func.name.gsub("#{type_name}_new", "_initialize")}" 55 | "rb_define_method(#{name}_class, \"#{initializer_name}\", &rb_#{type_name}#{initializer_name}, #{func.arguments.size - 1});" 56 | end 57 | methods = instance_methods.map do |func| 58 | method_name = "#{func.name.gsub("#{type_name}_", '')}" 59 | "rb_define_method(#{name}_class, \"#{method_name}\", &rb_#{type_name}_#{method_name}, #{func.arguments.size - 1});" 60 | end 61 | finalizer = "rb_define_singleton_method(#{name}_class, \"_finalize\", &rb_#{type_name}_finalize, 1);" 62 | type_func = "rb_define_method(#{name}_class, \"_type\", &rb_#{type_name}_type, 0);" 63 | [initializers, methods, finalizer, type_func].flatten 64 | end 65 | 66 | def constructors 67 | api_functions.select(&:constructor?) 68 | end 69 | 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /util/generator/class/struct.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | module Godot::Generator 3 | module Class 4 | class Struct < Base 5 | def instance_functions_from_header 6 | api_functions.select(&:instance_function?) 7 | end 8 | 9 | def api_functions 10 | headers_path = YAML.load(File.open("#{__dir__}/../../../env.yml", &:read))["headers_path"] 11 | json = JSON.parse File.open("#{headers_path}/gdnative_api.json", &:read) 12 | api_functions = json['core']['api'].select{|x| x['name'].match(/#{type_name}_/)} 13 | api_functions.map{|x| Godot::Generator::Function.new(self, x)}.select(&:valid?) 14 | end 15 | 16 | def instance_methods 17 | api_functions.select(&:method?) 18 | end 19 | 20 | def instance_functions 21 | instance_methods.map do |func| 22 | variants = func.arguments.select{|x| x.type.name == 'godot_variant_pointer'} 23 | 24 | params = func.arguments.map{|arg| "VALUE #{arg.name}"}.join(', ') 25 | args = func.arguments.map do |arg| 26 | if arg.variant_pointer? 27 | "&#{arg.name}_v" 28 | else 29 | arg.type.to_godot arg.name 30 | end 31 | end.join(', ') 32 | 33 | prepare_variants = variants.map do |arg| 34 | "godot_variant #{arg.name}_v = rb_godot_variant_to_godot(#{arg.name});" 35 | end.join("\n") 36 | clean_up_variants = variants.map do |arg| 37 | "api->godot_variant_destroy(&#{arg.name}_v);" 38 | end.join("\n") 39 | 40 | if func.return_type 41 | <<~EOF 42 | VALUE rb_#{func.name} (#{params}) { 43 | #{prepare_variants} 44 | #{func.return_type.signature} value = api->#{func.name}(#{args}); 45 | VALUE ret = #{func.return_type.from_godot('value')}; 46 | #{clean_up_variants} 47 | return ret; 48 | } 49 | EOF 50 | else 51 | <<~EOF 52 | VALUE rb_#{func.name} (#{params}) { 53 | #{prepare_variants} 54 | api->#{func.name}(#{args}); 55 | #{clean_up_variants} 56 | return Qnil; 57 | } 58 | EOF 59 | end 60 | end 61 | end 62 | 63 | def variant_type_enum_name 64 | "GODOT_VARIANT_TYPE_#{type_name.gsub("godot_", '')}".upcase 65 | end 66 | 67 | def variant_from_godot_branch 68 | <<~EOF 69 | case #{variant_type_enum_name}: { 70 | #{type_name} val = api->godot_variant_as_#{type_name.gsub('godot_', '')}(&addr); 71 | ret = #{Godot::Generator::Type.get_type(type_name).from_godot 'val'}; 72 | break; 73 | } 74 | EOF 75 | end 76 | 77 | def variant_to_godot_branch 78 | <<~EOF 79 | case #{variant_type_enum_name}: { 80 | #{type_name} *addr = #{Godot::Generator::Type.get_type("#{type_name} *").to_godot 'self'}; 81 | api->godot_variant_new_#{type_name.gsub('godot_', '')}(&var, addr); 82 | break; 83 | } 84 | EOF 85 | end 86 | 87 | def type_function 88 | <<~EOF 89 | VALUE rb_#{type_name}_type(VALUE self) { 90 | return LONG2FIX(#{variant_type_enum_name}); 91 | } 92 | EOF 93 | end 94 | 95 | def register_operator_statements 96 | instance_methods.select{|x| x.name.match(/operator_/)}.map do |x| 97 | opr_name = x.name.match(/operator_(.+)/)[1] 98 | case opr_name 99 | when 'add' 100 | opr = :+ 101 | when 'equal' 102 | opr = :== 103 | when 'subtract' 104 | opr = :- 105 | when 'multiply_vector', 'multiply_scalar', 'divide_vector', 'divide_scalar' 106 | next 107 | when 'less' 108 | opr = :< 109 | when 'neg' 110 | opr = :-@ 111 | when 'multiply' 112 | opr = :* 113 | when 'divide' 114 | opr = :/ 115 | when 'index' 116 | opr = :[] 117 | when 'index_const' 118 | next 119 | when 'plus' 120 | opr = :+ 121 | else 122 | raise opr_name 123 | end 124 | "rb_define_alias(#{name}_class, \"#{opr}\", \"operator_#{opr_name}\");" 125 | end.compact.join("\n") 126 | end 127 | 128 | def operator_methods 129 | [[:multiply, :*], [:divide, :/]].map do |opr_name, opr| 130 | functions = instance_methods.select{|x| x.name.match /operator_#{opr_name}/} 131 | if functions.size > 1 132 | dispatcher_method opr, functions 133 | end 134 | end.compact.join("\n") 135 | end 136 | 137 | def dispatcher_method name, functions, body = nil 138 | branches = functions.map do |func| 139 | when_statement = func.arguments_without_self.map.with_index do |arg, index| 140 | statement = arg.type.source_classes.map{|klass| "args[#{index}].is_a?(#{klass})"}.join(" || ") 141 | "(#{statement})" 142 | end.join(' && ') 143 | "when #{when_statement} then #{func.ruby_method_name}(*args)" 144 | end.join("\n") 145 | <<~EOF 146 | def #{name} *args 147 | case 148 | #{branches} 149 | else 150 | raise "mismatched arguments" 151 | end 152 | #{body} 153 | end 154 | EOF 155 | end 156 | 157 | end 158 | end 159 | end 160 | -------------------------------------------------------------------------------- /util/generator/classes.rb: -------------------------------------------------------------------------------- 1 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Aabb)) 2 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Basis)) 3 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Color)) 4 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Plane)) 5 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Quat)) 6 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Rect2)) 7 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Transform)) 8 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Transform2D)) 9 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Vector2)) 10 | Godot::Generator::Class.register_class(Godot::Generator::Class::Stack.new(:Vector3)) 11 | 12 | Godot::Generator::Class.register_class(Godot::Generator::Class::Array.new) 13 | Godot::Generator::Class.register_class(Godot::Generator::Class::Dictionary.new) 14 | Godot::Generator::Class.register_class(Godot::Generator::Class::NodePath.new) 15 | Godot::Generator::Class.register_class(Godot::Generator::Class::Object.new) 16 | Godot::Generator::Class.register_class(Godot::Generator::Class::PoolStringArray.new) 17 | Godot::Generator::Class.register_class(Godot::Generator::Class::String.new) 18 | -------------------------------------------------------------------------------- /util/generator/classes/array.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class Array < Godot::Generator::Class::Heap 4 | def initializer_function 5 | super <<~EOF 6 | api->godot_array_new(addr); 7 | 8 | for (int i=0; i < RARRAY_LEN(value); ++i) { 9 | godot_variant var = rb_godot_variant_to_godot(RARRAY_AREF(value, i)); 10 | api->godot_array_append(addr, &var); 11 | api->godot_variant_destroy(&var); 12 | } 13 | EOF 14 | end 15 | 16 | def instance_functions 17 | to_a = <<~EOF 18 | VALUE rb_#{type_name}_to_a(VALUE self) { 19 | godot_array *ary = rb_godot_array_pointer_to_godot(self); 20 | int size = api->godot_array_size(ary); 21 | 22 | VALUE ruby_ary = rb_ary_new(); 23 | 24 | for (int i=0; i < size; ++i) { 25 | godot_variant var = api->godot_array_get(ary, i); 26 | rb_ary_push(ruby_ary, rb_godot_variant_from_godot(var)); 27 | api->godot_variant_destroy(&var); 28 | } 29 | 30 | return ruby_ary; 31 | } 32 | EOF 33 | 34 | super.concat([to_a]) 35 | end 36 | 37 | def register_method_statements 38 | to_a = "rb_define_method(#{name}_class, \"to_a\", &rb_#{type_name}_to_a, 0);" 39 | 40 | super.concat([to_a]) 41 | end 42 | 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /util/generator/classes/dictionary.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class Dictionary < Godot::Generator::Class::Heap 4 | 5 | def initializer_function 6 | super <<~EOF 7 | api->godot_dictionary_new(addr); 8 | 9 | VALUE pairs = rb_funcall(value, rb_intern("to_a"), 0); 10 | 11 | for (int i=0; i < RARRAY_LEN(pairs); ++i) { 12 | godot_variant key, value; 13 | VALUE pair = RARRAY_AREF(pairs, i); 14 | key = rb_godot_variant_to_godot(RARRAY_AREF(pair, 0)); 15 | value = rb_godot_variant_to_godot(RARRAY_AREF(pair, 1)); 16 | api->godot_dictionary_set(addr, &key, &value); 17 | api->godot_variant_destroy(&key); 18 | api->godot_variant_destroy(&value); 19 | } 20 | EOF 21 | end 22 | 23 | def instance_functions 24 | to_h = <<~EOF 25 | VALUE rb_#{type_name}_to_h(VALUE self) { 26 | godot_dictionary *dict = rb_godot_dictionary_pointer_to_godot(self); 27 | 28 | godot_array keys = api->godot_dictionary_keys(dict); 29 | godot_array values = api->godot_dictionary_values(dict); 30 | 31 | VALUE r_keys = rb_godot_array_pointer_from_godot(&keys); 32 | VALUE r_values = rb_godot_array_pointer_from_godot(&values); 33 | VALUE r_keys_a = rb_funcall(r_keys, rb_intern("to_a"), 0); 34 | VALUE r_values_a = rb_funcall(r_values, rb_intern("to_a"), 0); 35 | VALUE zip = rb_funcall(r_keys_a, rb_intern("zip"), 1, r_values_a); 36 | 37 | api->godot_array_destroy(&keys); 38 | api->godot_array_destroy(&values); 39 | return rb_funcall(zip, rb_intern("to_h"), 0); 40 | } 41 | EOF 42 | 43 | super.concat([to_h]) 44 | end 45 | 46 | def register_method_statements 47 | to_h = "rb_define_method(#{name}_class, \"to_h\", &rb_#{type_name}_to_h, 0);" 48 | 49 | super.concat([to_h]) 50 | end 51 | 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /util/generator/classes/node_path.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class NodePath < Godot::Generator::Class::Heap 4 | 5 | def type_name 6 | 'godot_node_path' 7 | end 8 | 9 | def initializer_function 10 | super <<~EOF 11 | godot_string temp; 12 | VALUE is_godot_string = rb_funcall(value, rb_intern("is_a?"), 1, String_class); 13 | if (RTEST(is_godot_string)) { 14 | api->godot_string_new_copy(&temp, rb_godot_string_pointer_to_godot(value)); 15 | } else { 16 | api->godot_string_new(&temp); 17 | char* str = StringValuePtr(value); 18 | int len = RSTRING_LEN(value); 19 | 20 | api->godot_string_parse_utf8_with_len(&temp, str, len); 21 | } 22 | 23 | api->godot_node_path_new(addr, &temp); 24 | api->godot_string_destroy(&temp); 25 | EOF 26 | end 27 | 28 | def source_classes 29 | '::String' 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /util/generator/classes/object.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class Object < Godot::Generator::Class::Heap 4 | def initializer_function 5 | end 6 | 7 | def initialize_method 8 | end 9 | 10 | def register_method_statements 11 | methods = instance_methods.map do |func| 12 | method_name = "#{func.name.gsub("#{type_name}_", '')}" 13 | "rb_define_method(#{name}_class, \"#{method_name}\", &rb_#{type_name}_#{method_name}, #{func.arguments.size - 1});" 14 | end 15 | finalizer = "rb_define_singleton_method(#{name}_class, \"_finalize\", &rb_#{type_name}_finalize, 0);" 16 | type_func = "rb_define_method(#{name}_class, \"_type\", &rb_#{type_name}_type, 0);" 17 | [methods, finalizer, type_func].flatten 18 | end 19 | 20 | def variant_from_godot_branch 21 | <<~EOF 22 | case #{variant_type_enum_name}: { 23 | #{type_name}* val = api->godot_variant_as_#{type_name.gsub('godot_', '')}(&addr); 24 | ret = rb_godot_object_pointer_from_godot(val); 25 | break; 26 | } 27 | EOF 28 | end 29 | 30 | def variant_to_godot_branch 31 | <<~EOF 32 | case #{variant_type_enum_name}: { 33 | #{type_name} *addr = #{Godot::Generator::Type.get_type("#{type_name} *").to_godot 'self'}; 34 | api->godot_variant_new_#{type_name.gsub('godot_', '')}(&var, addr); 35 | break; 36 | } 37 | EOF 38 | end 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /util/generator/classes/pool_string_array.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class PoolStringArray < Godot::Generator::Class::Heap 4 | def initializer_function 5 | super <<~EOF 6 | api->#{type_name}_new(addr); 7 | 8 | for (int i=0; i < RARRAY_LEN(value); ++i) { 9 | godot_string *str = rb_godot_string_pointer_to_godot(RARRAY_AREF(value, i)); 10 | api->#{type_name}_append(addr, str); 11 | } 12 | EOF 13 | end 14 | 15 | def type_name 16 | 'godot_pool_string_array' 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /util/generator/classes/string.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Class 3 | class String < Godot::Generator::Class::Heap 4 | 5 | def initializer_function 6 | super <<~EOF 7 | VALUE is_godot_string = rb_funcall(value, rb_intern("is_a?"), 1, String_class); 8 | if (RTEST(is_godot_string)) { 9 | api->godot_string_new_copy(addr, rb_godot_string_pointer_to_godot(value)); 10 | } else { 11 | api->godot_string_new(addr); 12 | char* str = StringValuePtr(value); 13 | int len = RSTRING_LEN(value); 14 | 15 | api->godot_string_parse_utf8_with_len(addr, str, len); 16 | } 17 | EOF 18 | end 19 | 20 | def source_classes 21 | '::String' 22 | end 23 | 24 | def instance_functions 25 | to_s = <<~EOF 26 | VALUE rb_#{type_name}_to_s(VALUE self) { 27 | godot_string *str = rb_godot_string_pointer_to_godot(self); 28 | VALUE ruby_str; 29 | godot_char_string char_string = api->godot_string_utf8(str); 30 | const char *chars = api->godot_char_string_get_data(&char_string); 31 | int len = api->godot_char_string_length(&char_string); 32 | ruby_str = rb_utf8_str_new(chars, len); 33 | api->godot_char_string_destroy(&char_string); 34 | return ruby_str; 35 | } 36 | EOF 37 | 38 | super.concat([to_s]) 39 | end 40 | 41 | def register_method_statements 42 | to_s = "rb_define_method(#{name}_class, \"to_s\", &rb_#{type_name}_to_s, 0);" 43 | 44 | super.concat([to_s]) 45 | end 46 | 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /util/generator/function.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | class Function 3 | def initialize klass, defn 4 | @klass = klass 5 | @defn = defn 6 | end 7 | 8 | def name 9 | @defn['name'] 10 | end 11 | 12 | def return_type 13 | if @defn['return_type'] == 'void' 14 | nil 15 | else 16 | Godot::Generator::Type.get_type @defn['return_type'] 17 | end 18 | end 19 | 20 | def with_self? 21 | @defn['arguments'].first[1].match(/self/) 22 | end 23 | 24 | def instance_function? 25 | !name.match("_new") && arguments[0].name.match(/self/) 26 | end 27 | 28 | def ignore_types 29 | [ 30 | 'char *', 31 | 'godot_bool *', 32 | 'double', 33 | 'int64_t', 34 | 'godot_int *', 35 | 'void *', 36 | 'godot_string_name *', 37 | 'godot_char_string', 38 | 'int', 39 | 40 | 'godot_pool_byte_array', 41 | 'godot_pool_color_array *', 42 | 'godot_pool_vector3_array *', 43 | 'godot_pool_vector2_array *', 44 | 'godot_pool_real_array *', 45 | 'godot_pool_int_array *', 46 | 'godot_pool_byte_array *', 47 | 'godot_object *', 48 | 'uint64_t', 49 | 'godot_pool_string_array_read_access *', 50 | 'godot_pool_string_array_write_access *' 51 | ] 52 | end 53 | 54 | def ignore_functions 55 | [ 56 | 'godot_string_get_slicec', 57 | 'godot_dictionary_hash', 58 | 'godot_string_hash', 59 | 'godot_array_hash' 60 | ] 61 | end 62 | 63 | def arguments 64 | @defn['arguments'].map{|type, name| Argument.new(type, name)} 65 | end 66 | 67 | def arguments_without_self 68 | arguments[1..-1] 69 | end 70 | 71 | def method? 72 | instance_function? 73 | end 74 | 75 | def constructor? 76 | if @klass.is_a? Godot::Generator::Class::Stack 77 | name.match(/_new/) && arguments.size > 1 78 | else 79 | name.match(/_new/) 80 | end 81 | end 82 | 83 | def check_types 84 | if [@defn['return_type'], @defn['arguments'].map(&:first)].flatten.map{|x| x.gsub('const ', '')}.all?{|x| !ignore_types.include?(x)} 85 | return_type 86 | arguments.map(&:type) 87 | end 88 | end 89 | 90 | def check_ignore_function 91 | !ignore_functions.include?(name) 92 | end 93 | 94 | def valid? 95 | (constructor? || method?) && check_types && check_ignore_function 96 | end 97 | 98 | def ruby_method_name 99 | name.gsub("#{@klass.type_name}_new", "_initialize").gsub("#{@klass.type_name}_", '') 100 | end 101 | end 102 | 103 | end 104 | -------------------------------------------------------------------------------- /util/generator/function/argument.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | class Function 3 | class Argument 4 | attr_reader :name 5 | 6 | def initialize type, name 7 | @type = type 8 | @name = name 9 | end 10 | 11 | def type 12 | Godot::Generator::Type.get_type @type 13 | end 14 | 15 | def variant_pointer? 16 | type.name == 'godot_variant_pointer' 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /util/generator/type.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class << self 4 | attr_reader :types 5 | 6 | def register_type type 7 | @types ||= {} 8 | if type.respond_to?(:spawn_type) 9 | sibling_type = type.spawn_type 10 | if sibling_type 11 | @types[sibling_type.signature] = sibling_type 12 | end 13 | end 14 | @types[type.signature] = type 15 | end 16 | 17 | def get_type signature 18 | signature = signature.gsub('const ', '') 19 | type = @types[signature] 20 | raise "unknown type #{signature}" unless type 21 | type 22 | end 23 | 24 | def generate_godot_convert_functions 25 | @types.values.map do |t| 26 | [ 27 | t.respond_to?(:from_godot_function) && t.from_godot_function, 28 | t.respond_to?(:to_godot_function) && t.to_godot_function 29 | ] 30 | end.flatten.select(&:itself) 31 | end 32 | 33 | def generate_godot_convert_function_headers 34 | @types.values.map do |t| 35 | [ 36 | t.from_godot_function_header, 37 | t.to_godot_function_header 38 | ] 39 | end.flatten.select(&:itself) 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /util/generator/type/alias.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class Alias < Base 4 | def initialize signature, alias_signature 5 | @signature = signature 6 | @alias_signature = alias_signature 7 | end 8 | 9 | def from_godot_body name 10 | Godot::Generator::Type.get_type(@alias_signature).from_godot_body name 11 | end 12 | 13 | def to_godot_body name 14 | Godot::Generator::Type.get_type(@alias_signature).to_godot_body name 15 | end 16 | 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /util/generator/type/base.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class Base 4 | def initialize signature, **options 5 | @signature = signature 6 | @options = options 7 | end 8 | 9 | def signature 10 | @signature.gsub('const ', '') 11 | end 12 | 13 | def name 14 | @signature.gsub(' ', '_').gsub('*', 'pointer') 15 | end 16 | alias :type_name :name 17 | 18 | def source_classes 19 | [@options[:source_class]].flatten.compact 20 | end 21 | 22 | def from_godot_function 23 | <<~EOF 24 | VALUE rb_#{type_name}_from_godot (#{signature} addr) { 25 | return #{from_godot_body 'addr'}; 26 | } 27 | EOF 28 | end 29 | 30 | def from_godot_function_header 31 | "VALUE rb_#{type_name}_from_godot (#{signature});" 32 | end 33 | 34 | def to_godot_function 35 | <<~EOF if respond_to? :to_godot 36 | #{signature} rb_#{type_name}_to_godot (VALUE self) { 37 | return #{to_godot_body 'self'}; 38 | } 39 | EOF 40 | end 41 | 42 | def to_godot name 43 | "rb_#{type_name}_to_godot(#{name})" 44 | end 45 | 46 | def from_godot name 47 | "rb_#{type_name}_from_godot(#{name})" 48 | end 49 | 50 | def to_godot_function_header 51 | "#{signature} rb_#{type_name}_to_godot (VALUE);" 52 | end 53 | 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /util/generator/type/heap.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class Heap < Base 4 | def initialize signature 5 | @signature = signature 6 | end 7 | 8 | def from_godot_body name 9 | "rb_#{self.name}_pointer_from_godot(&#{name})" 10 | end 11 | 12 | def to_godot_body name 13 | "*rb_#{self.name}_pointer_to_godot(#{name})" 14 | end 15 | 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /util/generator/type/heap_pointer.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class HeapPointer < Struct 4 | def from_godot_function 5 | <<~EOF 6 | VALUE rb_#{type_name}_from_godot (#{signature} addr) { 7 | #{signature} naddr = api->godot_alloc(sizeof(#{signature_without_star})); 8 | memcpy(naddr, addr, sizeof(#{signature_without_star})); 9 | // will copy increase the reference count? 10 | // api->#{type_name.gsub('_pointer', '')}_new_copy(naddr, addr); 11 | VALUE obj = rb_funcall(#{target_class_name}_class, rb_intern("_adopt"), 1, LONG2NUM((long)naddr)); 12 | return obj; 13 | } 14 | EOF 15 | end 16 | 17 | def to_godot_function 18 | <<~EOF 19 | #{signature} rb_#{type_name}_to_godot (VALUE self) { 20 | VALUE addr = rb_iv_get(self, "@_godot_address"); 21 | return (#{signature})NUM2LONG(addr); 22 | } 23 | EOF 24 | end 25 | 26 | def spawn_type 27 | Godot::Generator::Type::Heap.new(signature.gsub(' *', '')) 28 | end 29 | 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /util/generator/type/stack.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class Stack < Base 4 | def initialize signature 5 | @signature = signature 6 | end 7 | 8 | def from_godot_body name 9 | "rb_#{self.name}_pointer_from_godot(&#{name})" 10 | end 11 | 12 | def to_godot_body name 13 | "*rb_#{self.name}_pointer_to_godot(#{name})" 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /util/generator/type/stack_pointer.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class StackPointer < Struct 4 | def from_godot_function 5 | <<~EOF 6 | VALUE rb_#{type_name}_from_godot (#{signature} addr) { 7 | #{signature} naddr = api->godot_alloc(sizeof(#{signature_without_star})); 8 | memcpy(naddr, addr, sizeof(#{signature_without_star})); 9 | VALUE obj = rb_funcall(#{target_class_name}_class, rb_intern("_adopt"), 1, LONG2NUM((long)naddr)); 10 | return obj; 11 | } 12 | EOF 13 | end 14 | 15 | def to_godot_function 16 | <<~EOF 17 | #{signature} rb_#{type_name}_to_godot (VALUE self) { 18 | VALUE addr = rb_iv_get(self, "@_godot_address"); 19 | return (#{signature})NUM2LONG(addr); 20 | } 21 | EOF 22 | end 23 | 24 | def source_classes 25 | super + ["Godot::#{target_class_name}"] 26 | end 27 | 28 | def spawn_type 29 | Godot::Generator::Type::Stack.new(signature.gsub(' *', '')) 30 | end 31 | 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /util/generator/type/struct.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class Struct < Base 4 | def initialize signature, **options 5 | @signature = signature 6 | @options = options 7 | end 8 | 9 | def target_class 10 | Godot::Generator::Class.get_class(@options[:target_class]) 11 | end 12 | 13 | def target_class_name 14 | target_class.name.to_s 15 | end 16 | 17 | def signature_without_star 18 | signature.gsub('*', '') 19 | end 20 | 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /util/generator/types.rb: -------------------------------------------------------------------------------- 1 | Godot::Generator::Type.register_type(Godot::Generator::Type::GodotBool.new) 2 | 3 | Godot::Generator::Type.register_type(Godot::Generator::Type::GodotReal.new) 4 | 5 | Godot::Generator::Type.register_type(Godot::Generator::Type::GodotInt.new) 6 | Godot::Generator::Type.register_type(Godot::Generator::Type::Alias.new('godot_vector3_axis', 'godot_int')) 7 | Godot::Generator::Type.register_type(Godot::Generator::Type::Alias.new('signed char', 'godot_int')) 8 | Godot::Generator::Type.register_type(Godot::Generator::Type::Alias.new('uint32_t', 'godot_int')) 9 | Godot::Generator::Type.register_type(Godot::Generator::Type::Alias.new('godot_error', 'godot_int')) 10 | 11 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 12 | 'godot_aabb *', 13 | target_class: 'Aabb', 14 | )) 15 | 16 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 17 | 'godot_basis *', 18 | target_class: 'Basis' 19 | )) 20 | 21 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 22 | 'godot_color *', 23 | target_class: 'Color' 24 | )) 25 | 26 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 27 | 'godot_plane *', 28 | target_class: 'Plane' 29 | )) 30 | 31 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 32 | 'godot_quat *', 33 | target_class: 'Quat' 34 | )) 35 | 36 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 37 | 'godot_rect2 *', 38 | target_class: 'Rect2' 39 | )) 40 | 41 | Godot::Generator::Type.register_type(Godot::Generator::Type::HeapPointer.new( 42 | 'godot_string *', 43 | target_class: 'String', 44 | source_class: ['::String', 'Godot::String'] 45 | )) 46 | 47 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 48 | 'godot_transform *', 49 | target_class: 'Transform' 50 | )) 51 | 52 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 53 | 'godot_transform2d *', 54 | target_class: 'Transform2D' 55 | )) 56 | 57 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 58 | 'godot_vector2 *', 59 | target_class: 'Vector2' 60 | )) 61 | 62 | Godot::Generator::Type.register_type(Godot::Generator::Type::StackPointer.new( 63 | 'godot_vector3 *', 64 | target_class: 'Vector3' 65 | )) 66 | 67 | Godot::Generator::Type.register_type(Godot::Generator::Type::HeapPointer.new( 68 | 'godot_node_path *', 69 | target_class: 'NodePath', 70 | source_class: ['::String', 'Godot::String'] 71 | )) 72 | 73 | Godot::Generator::Type.register_type(Godot::Generator::Type::HeapPointer.new( 74 | 'godot_array *', 75 | target_class: 'Array', 76 | source_class: ['::Array', 'Godot::Array'] 77 | )) 78 | 79 | Godot::Generator::Type.register_type(Godot::Generator::Type::HeapPointer.new( 80 | 'godot_dictionary *', 81 | target_class: 'Dictionary', 82 | source_class: ['::Hash', 'Godot::Dictionary'] 83 | )) 84 | 85 | Godot::Generator::Type.register_type(Godot::Generator::Type::GodotObjectPointer.new) 86 | 87 | Godot::Generator::Type.register_type(Godot::Generator::Type::HeapPointer.new( 88 | 'godot_pool_string_array *', 89 | target_class: 'PoolStringArray', 90 | source_class: [] 91 | )) 92 | 93 | Godot::Generator::Type.register_type(Godot::Generator::Type::GodotVariant.new) 94 | Godot::Generator::Type.register_type(Godot::Generator::Type::GodotVariantPointer.new) 95 | 96 | Godot::Generator::Type.register_type(Godot::Generator::Type::WcharTPointer.new) 97 | Godot::Generator::Type.register_type(Godot::Generator::Type::WcharT.new) 98 | 99 | =begin 100 | 101 | 102 | Godot::Generator::Type.register_type( 103 | 'godot_array', 104 | from_godot: -> name { 105 | "from_array" 106 | }, 107 | to_godot: -> name { 108 | "to_array" 109 | } 110 | ) 111 | 112 | 113 | Godot::Generator::Type.register_type( 114 | 'godot_variant', 115 | from_godot: -> name { 116 | "from_array" 117 | }, 118 | to_godot: -> name { 119 | "to_array" 120 | } 121 | ) 122 | 123 | Godot::Generator::Type.register_type( 124 | 'godot_variant *', 125 | from_godot: -> name { 126 | "from_array" 127 | }, 128 | to_godot: -> name { 129 | "to_array" 130 | } 131 | ) 132 | 133 | Godot::Generator::Type.register_type( 134 | 'godot_pool_byte_array', 135 | from_godot: -> name { 136 | "from_array" 137 | }, 138 | to_godot: -> name { 139 | "to_array" 140 | } 141 | ) 142 | =end 143 | -------------------------------------------------------------------------------- /util/generator/types/godot_bool.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class GodotBool < Base 4 | def initialize 5 | @signature = 'godot_bool' 6 | end 7 | 8 | def to_godot_body name 9 | "(RTEST(#{name}) ? GODOT_TRUE : GODOT_FALSE)" 10 | end 11 | 12 | def from_godot_body name 13 | "(#{name} ? Qtrue: Qfalse)" 14 | end 15 | 16 | def source_classes 17 | ['TrueClass', 'FalseClass'] 18 | end 19 | end 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /util/generator/types/godot_int.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class GodotInt < Base 4 | def initialize 5 | @signature = 'godot_int' 6 | end 7 | 8 | def to_godot_body name 9 | "NUM2LONG(#{name})" 10 | end 11 | 12 | def from_godot_body name 13 | "LONG2NUM(#{name})" 14 | end 15 | 16 | def source_classes 17 | ['Numeric'] 18 | end 19 | end 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /util/generator/types/godot_object_pointer.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class GodotObjectPointer < HeapPointer 4 | def initialize 5 | super( 6 | 'godot_object *', 7 | target_class: 'Object' 8 | ) 9 | end 10 | 11 | # emmmm godot_object* is merely a void* 12 | def from_godot_function 13 | <<~EOF 14 | VALUE rb_#{type_name}_from_godot (#{signature} addr) { 15 | VALUE obj = rb_funcall(#{target_class_name}_class, rb_intern("_adopt"), 1, LONG2NUM((long)addr)); 16 | return obj; 17 | } 18 | EOF 19 | end 20 | 21 | def spawn_type 22 | end 23 | 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /util/generator/types/godot_real.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class GodotReal < Base 4 | def initialize 5 | @signature = 'godot_real' 6 | end 7 | 8 | def to_godot_body name 9 | "NUM2DBL(#{name})" 10 | end 11 | 12 | def from_godot_body name 13 | "DBL2NUM(#{name})" 14 | end 15 | 16 | def source_classes 17 | ['Numeric'] 18 | end 19 | end 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /util/generator/types/godot_variant.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class GodotVariant < Base 4 | def initialize 5 | @signature = 'godot_variant' 6 | end 7 | 8 | def from_godot_function 9 | <<~EOF 10 | VALUE rb_godot_variant_from_godot (godot_variant addr) { 11 | VALUE ret; 12 | switch (api->godot_variant_get_type(&addr)) { 13 | case GODOT_VARIANT_TYPE_NIL: 14 | ret = Qnil; 15 | break; 16 | case GODOT_VARIANT_TYPE_BOOL: 17 | ret = #{Godot::Generator::Type.get_type('godot_bool').from_godot '(api->godot_variant_as_bool(&addr))'}; 18 | break; 19 | case GODOT_VARIANT_TYPE_INT: 20 | ret = #{Godot::Generator::Type.get_type('godot_int').from_godot '(api->godot_variant_as_int(&addr))'}; 21 | break; 22 | case GODOT_VARIANT_TYPE_REAL: 23 | ret = #{Godot::Generator::Type.get_type('godot_real').from_godot '(api->godot_variant_as_real(&addr))'}; 24 | break; 25 | #{Godot::Generator::Class.classes.values.map{|c| c.variant_from_godot_branch}.join("\n")} 26 | } 27 | //api->godot_variant_destroy(&addr); 28 | return ret; 29 | } 30 | EOF 31 | end 32 | 33 | def to_godot_function 34 | <<~EOF 35 | godot_variant rb_godot_variant_to_godot (VALUE self) { 36 | godot_variant var; 37 | switch (TYPE(self)) { 38 | case T_NIL: { 39 | api->godot_variant_new_nil(&var); 40 | break; 41 | } 42 | case T_TRUE: { 43 | api->godot_variant_new_bool(&var, 1); 44 | break; 45 | } 46 | case T_FALSE: { 47 | api->godot_variant_new_bool(&var, 0); 48 | break; 49 | } 50 | case T_FIXNUM: { 51 | api->godot_variant_new_int(&var, FIX2LONG(self)); 52 | break; 53 | } 54 | case T_STRING: { 55 | VALUE r_str = rb_funcall(String_class, rb_intern("new"), 1, self); 56 | godot_string *str = rb_godot_string_pointer_to_godot(r_str); 57 | api->godot_variant_new_string(&var, str); 58 | break; 59 | } 60 | case T_FLOAT: { 61 | api->godot_variant_new_real(&var, RFLOAT_VALUE(self)); 62 | break; 63 | } 64 | case T_SYMBOL: { 65 | VALUE _str = rb_funcall(self, rb_intern("to_s"), 0); 66 | VALUE r_str = rb_funcall(String_class, rb_intern("new"), 1, _str); 67 | godot_string *str = rb_godot_string_pointer_to_godot(r_str); 68 | api->godot_variant_new_string(&var, str); 69 | break; 70 | } 71 | case T_ARRAY: { 72 | VALUE r_ary = rb_funcall(Array_class, rb_intern("new"), 1, self); 73 | godot_array *ary = rb_godot_array_pointer_to_godot(r_ary); 74 | api->godot_variant_new_array(&var, ary); 75 | break; 76 | } 77 | case T_HASH: { 78 | VALUE hsh = rb_funcall(Dictionary_class, rb_intern("new"), 1, self); 79 | godot_dictionary *dict = rb_godot_dictionary_pointer_to_godot(hsh); 80 | api->godot_variant_new_dictionary(&var, dict); 81 | break; 82 | } 83 | default: { 84 | VALUE godot_module = rb_const_get(rb_cModule, rb_intern("Godot")); 85 | VALUE built_in_type_class = rb_funcall(godot_module, rb_intern("built_in_type_class"), 0); 86 | 87 | if (RTEST(rb_funcall(self, rb_intern("is_a?"), 1, built_in_type_class))) { 88 | switch (FIX2LONG(rb_funcall(self, rb_intern("_type"), 0))) { 89 | #{Godot::Generator::Class.classes.values.map{|c| c.variant_to_godot_branch}.join("\n")} 90 | default: { 91 | api->godot_print_error("unknown variant type", "", __FILE__, __LINE__); 92 | api->godot_variant_new_nil(&var); 93 | } 94 | } 95 | } else { 96 | VALUE _str = rb_funcall(self, rb_intern("to_s"), 0); 97 | VALUE r_str = rb_funcall(String_class, rb_intern("new"), 1, _str); 98 | godot_string *str = rb_godot_string_pointer_to_godot(r_str); 99 | api->godot_variant_new_string(&var, str); 100 | } 101 | } 102 | } 103 | 104 | return var; 105 | } 106 | EOF 107 | end 108 | 109 | def source_classes 110 | ['Numeric'] 111 | end 112 | end 113 | 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /util/generator/types/godot_variant_pointer.rb: -------------------------------------------------------------------------------- 1 | module Godot::Generator 2 | module Type 3 | class GodotVariantPointer < Base 4 | def initialize 5 | @signature = 'godot_variant *' 6 | end 7 | 8 | def to_godot_body name 9 | "&rb_godot_variant_to_godot(#{name})" 10 | end 11 | 12 | def from_godot_body name 13 | "rb_godot_variant_from_godot(*#{name})" 14 | end 15 | 16 | def source_classes 17 | [] 18 | end 19 | 20 | def to_godot_function 21 | end 22 | 23 | def to_godot_function_header 24 | end 25 | end 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /util/generator/types/wchar_t.rb: -------------------------------------------------------------------------------- 1 | 2 | module Godot::Generator 3 | module Type 4 | class WcharT < Base 5 | def initialize 6 | @signature = 'wchar_t' 7 | end 8 | 9 | def to_godot_body name 10 | "NUM2DBL(#{name})" 11 | end 12 | 13 | def from_godot_body name 14 | "rb_wchar_t_pointer_from_godot(&#{name})" 15 | end 16 | 17 | end 18 | 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /util/generator/types/wchar_t_pointer.rb: -------------------------------------------------------------------------------- 1 | 2 | module Godot::Generator 3 | module Type 4 | class WcharTPointer < Base 5 | def initialize 6 | @signature = 'wchar_t *' 7 | end 8 | 9 | def from_godot_function 10 | <<~EOF 11 | VALUE rb_wchar_t_pointer_from_godot (wchar_t *addr) { 12 | godot_string str; 13 | api->godot_string_new_with_wide_string(&str, addr, 1); 14 | VALUE obj = rb_funcall(String_class, rb_intern("_adopt"), 1, LONG2NUM((long)&str)); 15 | return obj; 16 | } 17 | EOF 18 | end 19 | 20 | def to_godot_function 21 | end 22 | end 23 | 24 | end 25 | end 26 | --------------------------------------------------------------------------------