├── bin ├── linux │ └── .gitkeep ├── macos │ └── .gitkeep ├── android │ └── .gitkeep └── windows │ └── .gitkeep ├── demo ├── addons │ └── gdluau │ │ ├── bin │ │ ├── linux │ │ │ └── .gitkeep │ │ ├── macos │ │ │ └── .gitkeep │ │ └── windows │ │ │ └── .gitkeep │ │ ├── gdluau.gdextension │ │ └── LICENSE ├── .gitignore ├── .gitattributes ├── LuauVM.gd ├── luau_vm.tscn ├── main.tscn ├── project.godot ├── icon.svg.import ├── main.gd └── icon.svg ├── .gitattributes ├── .vscode └── extensions.json ├── .gitmodules ├── register_types.h ├── src ├── register_types.h ├── vector_lib.h ├── classes │ ├── luau_function_result.h │ ├── luau_function.h │ ├── luau_function_result.cpp │ ├── luau_function.cpp │ ├── luau_vm.h │ ├── luau_vm.cpp │ └── luau_vm_bindings.cpp ├── utils.h ├── register_types.cpp ├── vector_lib.cpp └── utils.cpp ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── bug_report.yml └── workflows │ └── builds.yml ├── LICENSE ├── .gitignore ├── register_types.cpp ├── README.md ├── SConstruct └── .clang-format /bin/linux/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/macos/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/android/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/windows/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/addons/gdluau/bin/linux/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/addons/gdluau/bin/macos/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/addons/gdluau/bin/windows/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /demo/.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /demo/LuauVM.gd: -------------------------------------------------------------------------------- 1 | extends LuauVM 2 | 3 | 4 | func _ready(): 5 | pass 6 | 7 | func _on_stdout(message): 8 | print("VM:", message) 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.cpptools-extension-pack", 4 | "ms-python.python" 5 | ] 6 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "godot-cpp"] 2 | path = godot-cpp 3 | url = https://github.com/godotengine/godot-cpp.git 4 | branch = master 5 | [submodule "luau"] 6 | path = luau 7 | url = https://github.com/Manonox/luau.git 8 | -------------------------------------------------------------------------------- /demo/luau_vm.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://c1725j5vsidhq"] 2 | 3 | [ext_resource type="Script" path="res://LuauVM.gd" id="1_q513d"] 4 | 5 | [node name="LuauVM" type="LuauVM"] 6 | script = ExtResource("1_q513d") 7 | 8 | [connection signal="stdout" from="." to="." method="_on_stdout"] 9 | -------------------------------------------------------------------------------- /register_types.h: -------------------------------------------------------------------------------- 1 | #ifndef GDLUAU_REGISTER_TYPES_H 2 | #define GDLUAU_REGISTER_TYPES_H 3 | 4 | #include 5 | 6 | using namespace godot; 7 | 8 | void initialize_gdluau_module(ModuleInitializationLevel p_level); 9 | void uninitialize_gdluau_module(ModuleInitializationLevel p_level); 10 | 11 | #endif // GDLUAU_REGISTER_TYPES_H 12 | -------------------------------------------------------------------------------- /demo/main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://ba8oxajrv3g3x"] 2 | 3 | [ext_resource type="Script" path="res://main.gd" id="1_fr1je"] 4 | 5 | [node name="Main" type="Node"] 6 | script = ExtResource("1_fr1je") 7 | 8 | [node name="LuauVM" type="LuauVM" parent="."] 9 | 10 | [connection signal="stdout" from="LuauVM" to="." method="_on_luau_vm_stdout"] 11 | -------------------------------------------------------------------------------- /src/register_types.h: -------------------------------------------------------------------------------- 1 | #ifndef GDLUAU_REGISTER_TYPES_H 2 | #define GDLUAU_REGISTER_TYPES_H 3 | 4 | #include 5 | 6 | using namespace godot; 7 | 8 | void initialize_gdluau_module(ModuleInitializationLevel p_level); 9 | void uninitialize_gdluau_module(ModuleInitializationLevel p_level); 10 | 11 | #endif // GDLUAU_REGISTER_TYPES_H 12 | -------------------------------------------------------------------------------- /src/vector_lib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | #define LUA_VECLIBNAME "vector" 8 | 9 | 10 | int lua_vector_lib_call(lua_State *L); 11 | 12 | int lua_vector_tostring(lua_State *L); 13 | 14 | int lua_vector_index(lua_State *L); 15 | int lua_vector_newindex(lua_State *L); 16 | 17 | int luaopen_vector(lua_State *L); 18 | -------------------------------------------------------------------------------- /demo/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=5 10 | 11 | [application] 12 | 13 | config/name="godot cpp template" 14 | run/main_scene="res://luau_vm.tscn" 15 | config/features=PackedStringArray("4.2", "Forward Plus") 16 | config/icon="res://icon.svg" 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | - name: Godot proposals 5 | url: https://github.com/godotengine/godot-proposals 6 | about: Please submit feature proposals on the Godot proposals repository, not here. 7 | 8 | - name: Godot documentation repository 9 | url: https://github.com/godotengine/godot-docs 10 | about: Please report issues with documentation on the Godot documentation repository, not here. 11 | 12 | - name: Godot community channels 13 | url: https://godotengine.org/community 14 | about: Please ask for technical support on one of the other community channels, not here. 15 | -------------------------------------------------------------------------------- /src/classes/luau_function_result.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace godot { 9 | 10 | class LuauFunctionResult : public RefCounted { 11 | GDCLASS(LuauFunctionResult, RefCounted) 12 | 13 | private: 14 | Array tuple; 15 | String error; 16 | bool _is_error = false; 17 | 18 | protected: 19 | static void _bind_methods(); 20 | 21 | public: 22 | LuauFunctionResult() {} 23 | LuauFunctionResult(const Array &tuple); 24 | LuauFunctionResult(const String &error); 25 | ~LuauFunctionResult() {} 26 | 27 | Array get_tuple(); 28 | String get_error(); 29 | bool is_error(); 30 | }; 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /demo/addons/gdluau/gdluau.gdextension: -------------------------------------------------------------------------------- 1 | [configuration] 2 | 3 | entry_symbol = "gdluau_library_init" 4 | compatibility_minimum = "4.2" 5 | 6 | [libraries] 7 | 8 | windows.debug.x86_64 = "res://addons/gdluau/bin/windows/gdluau.windows.template_debug.x86_64.dll" 9 | windows.release.x86_64 = "res://addons/gdluau/bin/windows/gdluau.windows.template_release.x86_64.dll" 10 | linux.debug.x86_64 = "res://addons/gdluau/bin/linux/libgdluau.linux.template_debug.x86_64.so" 11 | linux.release.x86_64 = "res://addons/gdluau/bin/linux/libgdluau.linux.template_release.x86_64.so" 12 | macos.debug = "res://addons/gdluau/bin/macos/macos.framework/libgdluau.macos.template_debug" 13 | macos.release = "res://addons/gdluau/bin/macos/macos.framework/libgdluau.macos.template_release" -------------------------------------------------------------------------------- /src/classes/luau_function.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | 10 | 11 | namespace godot { 12 | 13 | class LuauFunction : public RefCounted { 14 | GDCLASS(LuauFunction, RefCounted) 15 | 16 | private: 17 | lua_State *L = nullptr; 18 | int ref = LUA_REFNIL; 19 | 20 | protected: 21 | static void _bind_methods(); 22 | 23 | public: 24 | LuauFunction() {} 25 | LuauFunction(lua_State *L, int ref); 26 | ~LuauFunction(); 27 | 28 | Ref pcall(const Variant **args, GDExtensionInt arg_count, GDExtensionCallError &error); 29 | Ref pcallv(const Array &arguments); 30 | 31 | private: 32 | Ref pcall_internal(int arg_count); 33 | }; 34 | 35 | } 36 | 37 | 38 | godot::Ref lua_tofunction(lua_State *L, int idx); 39 | -------------------------------------------------------------------------------- /demo/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dbx66sovxd1" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /src/classes/luau_function_result.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | using namespace godot; 8 | 9 | LuauFunctionResult::LuauFunctionResult(const Array &tuple) { 10 | this->tuple = tuple; 11 | this->_is_error = false; 12 | } 13 | 14 | LuauFunctionResult::LuauFunctionResult(const String &error) { 15 | this->error = error; 16 | this->_is_error = true; 17 | } 18 | 19 | Array LuauFunctionResult::get_tuple() { 20 | if (_is_error) return Variant(); 21 | return this->tuple; 22 | } 23 | 24 | String LuauFunctionResult::get_error() { 25 | if (!_is_error) return Variant(); 26 | return this->error; 27 | } 28 | 29 | bool LuauFunctionResult::is_error() { 30 | return this->_is_error; 31 | } 32 | 33 | 34 | void LuauFunctionResult::_bind_methods() { 35 | ClassDB::bind_method(D_METHOD("get_error"), &LuauFunctionResult::get_error); 36 | ClassDB::bind_method(D_METHOD("get_tuple"), &LuauFunctionResult::get_tuple); 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tigran Mamedov 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 | -------------------------------------------------------------------------------- /demo/addons/gdluau/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tigran Mamedov 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 | -------------------------------------------------------------------------------- /demo/main.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | 4 | @onready var vm : LuauVM = $LuauVM 5 | func _ready(): 6 | vm.open_all_libraries() 7 | 8 | vm.stdout.connect(_stdout) 9 | # Puts the number on top of the stack 10 | vm.lua_pushnumber(3) 11 | # Pops the top of the stack (our number) and sets a global variable "x" 12 | vm.lua_setglobal("x") 13 | # Compiles and executes a string of lua code 14 | var success := vm.lua_dostring(""" 15 | local t = { 16 | shit = 3, 17 | } 18 | 19 | print("t = ", t) 20 | print("t.shit = ", t.shit) 21 | 22 | y = x + t.shit 23 | """) 24 | 25 | if success != vm.LUA_OK: 26 | # An error happened, pop an error string off the stack and print it to godot console 27 | print(vm.lua_tostring(-1)) 28 | vm.lua_pop() 29 | return 30 | 31 | # Put the value of a global variable "y" on top of the stack 32 | vm.lua_getglobal("y") 33 | # "Convert" a lua number on top of the stack to a godot float 34 | var result := vm.lua_tonumber(-1) 35 | # Clear the stack (stack only has a single number in it) 36 | vm.lua_pop() 37 | 38 | print("Result (y) = %s" % result) 39 | 40 | 41 | func _stdout(message: String) -> void: 42 | print("Lua says: %s" % message) 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | 4 | luau/*.obj 5 | luau/*.o 6 | 7 | # Ignore library files but not the gdextension file 8 | demo/addons/gdluau/bin/* 9 | !demo/addons/gdluau/bin/android 10 | demo/addons/gdluau/bin/android/* 11 | !demo/addons/gdluau/bin/android/.gitkeep 12 | !demo/addons/gdluau/bin/linux 13 | demo/addons/gdluau/bin/linux/* 14 | !demo/addons/gdluau/bin/linux/.gitkeep 15 | !demo/addons/gdluau/bin/macos 16 | demo/addons/gdluau/bin/macos/* 17 | !demo/addons/gdluau/bin/macos/.gitkeep 18 | !demo/addons/gdluau/bin/windows 19 | demo/addons/gdluau/bin/windows/* 20 | !demo/addons/gdluau/bin/windows/.gitkeep 21 | !demo/addons/gdluau/bin/*.gdextension 22 | 23 | bin/android/* 24 | !bin/android/.gitkeep 25 | bin/linux/* 26 | !bin/linux/.gitkeep 27 | bin/macos/* 28 | !bin/macos/.gitkeep 29 | bin/windows/* 30 | !bin/windows/.gitkeep 31 | 32 | *.dll 33 | 34 | .sconsign*.dblite 35 | 36 | # Ignore custom.py 37 | custom.py 38 | 39 | # Ignore generated compile_commands.json 40 | compile_commands.json 41 | 42 | # Binaries 43 | *.o 44 | *.a 45 | *.os 46 | *.so 47 | *.obj 48 | *.bc 49 | *.pyc 50 | *.dblite 51 | *.pdb 52 | *.lib 53 | *.config 54 | *.creator 55 | *.creator.user 56 | *.files 57 | *.includes 58 | *.idb 59 | *.exp 60 | 61 | # Other stuff 62 | *.log 63 | 64 | # VSCode 65 | .vscode/* 66 | !.vscode/extensions.json 67 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | void lua_pushvariant(lua_State *L, const godot::Variant &var); 13 | void lua_pusharray(lua_State *L, const godot::Array &array); 14 | void lua_pushdictionary(lua_State *L, const godot::Dictionary &dict); 15 | 16 | godot::Variant lua_tovariant(lua_State *L, int idx); 17 | godot::Variant lua_toarray(lua_State *L, int idx); 18 | godot::Variant lua_todictionary(lua_State *L, int idx); 19 | 20 | bool luaL_isarray(lua_State *L, int idx); 21 | 22 | void lua_pushcallable(lua_State *L, const godot::Callable &callable, const godot::String &debugname); 23 | 24 | 25 | bool luaL_hasmetatable(lua_State *L, int idx, const char *tname); 26 | 27 | void lua_pushobject(lua_State *L, godot::Object *object); 28 | void object_userdata_dtor(lua_State *L, void *data); 29 | godot::Object *lua_toobject(lua_State *L, int idx); 30 | int lua_isobject(lua_State *L, int idx); 31 | bool lua_isvalidobject(lua_State *L, int idx); 32 | godot::Object *luaL_checkobject(lua_State *L, int idx, bool valid); 33 | 34 | 35 | 36 | #define GDLUAU_REGISTRY_NODE_KEY "gdluau_node" 37 | void lua_setnode(lua_State* L, godot::LuauVM* node); 38 | godot::LuauVM* lua_getnode(lua_State* L); 39 | -------------------------------------------------------------------------------- /register_types.cpp: -------------------------------------------------------------------------------- 1 | #include "register_types.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "src/classes/luau_vm.h" 10 | // #include "src/classes/luau_error.h" 11 | // #include "src/classes/luau_function.h" 12 | 13 | using namespace godot; 14 | 15 | void initialize_gdluau_module(ModuleInitializationLevel p_level) { 16 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 17 | return; 18 | } 19 | 20 | ClassDB::register_class(); 21 | ClassDB::register_class(); 22 | ClassDB::register_class(); 23 | } 24 | 25 | void uninitialize_gdluau_module(ModuleInitializationLevel p_level) { 26 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 27 | return; 28 | } 29 | } 30 | 31 | extern "C" { 32 | // Initialization. 33 | GDExtensionBool GDE_EXPORT gdluau_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { 34 | godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); 35 | 36 | init_obj.register_initializer(initialize_gdluau_module); 37 | init_obj.register_terminator(uninitialize_gdluau_module); 38 | init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); 39 | 40 | return init_obj.init(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/register_types.cpp: -------------------------------------------------------------------------------- 1 | #include "register_types.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "classes/luau_vm.h" 10 | // #include "src/classes/luau_error.h" 11 | // #include "src/classes/luau_function.h" 12 | 13 | using namespace godot; 14 | 15 | void initialize_gdluau_module(ModuleInitializationLevel p_level) { 16 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 17 | return; 18 | } 19 | 20 | ClassDB::register_class(); 21 | ClassDB::register_class(); 22 | ClassDB::register_class(); 23 | } 24 | 25 | void uninitialize_gdluau_module(ModuleInitializationLevel p_level) { 26 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 27 | return; 28 | } 29 | } 30 | 31 | extern "C" { 32 | // Initialization. 33 | GDExtensionBool GDE_EXPORT gdluau_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { 34 | godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); 35 | 36 | init_obj.register_initializer(initialize_gdluau_module); 37 | init_obj.register_terminator(uninitialize_gdluau_module); 38 | init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); 39 | 40 | return init_obj.init(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Report a bug with the godot-cpp template 3 | body: 4 | 5 | - type: markdown 6 | attributes: 7 | value: | 8 | - Write a descriptive issue title above. 9 | - Search [open](https://github.com/godotengine/godot-cpp-template/issues) and [closed](https://github.com/godotengine/godot-cpp-template/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported. 10 | - type: input 11 | attributes: 12 | label: Godot version 13 | description: > 14 | Specify the Git commit hash of your Godot build. 15 | placeholder: v4.0.stable.official [92bee43ad] 16 | validations: 17 | required: true 18 | 19 | - type: input 20 | attributes: 21 | label: godot-cpp version 22 | description: > 23 | Specify the Git commit hash of the godot-cpp submodule in your project. You can run `git status` inside the folder to check it. 24 | placeholder: v4.0.stable.official [9d1c396c5] 25 | validations: 26 | required: true 27 | 28 | - type: input 29 | attributes: 30 | label: System information 31 | description: | 32 | Specify the OS version. 33 | placeholder: Windows 10 34 | validations: 35 | required: true 36 | 37 | - type: textarea 38 | attributes: 39 | label: Issue description 40 | description: | 41 | Describe your issue briefly. What doesn't work, and how do you expect it to work instead? 42 | You can include images or videos with drag and drop, and format code blocks or logs with ``` tags. 43 | validations: 44 | required: true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GDLuau 2 | A GDExtension that adds [Luau](https://luau-lang.org) C/C++ API bindings to GDScript 3 | *(+ some extras)* 4 | 5 | > **Warning** 6 | > 7 | > This repository's `main` branch is only usable with 8 | > [GDExtension](https://godotengine.org/article/introducing-gd-extensions) 9 | > from Godot's `master` branch. 10 | 11 | About 12 | ----- 13 | GDLuau is for in-game scripting / UGC / modding. 14 | *(This is **not** a replacement for or an alternative to GDScript.)* 15 | 16 | Luau API closely follows Lua 5.1 API, you can find the manual [here](https://www.lua.org/manual/5.1/manual.html).
17 | For coding in lua/luau take a look at the ["Getting Started" section](https://luau-lang.org/getting-started) on [luau-lang.org](https://luau-lang.org).
18 | 19 | 20 | Features 21 | -------- 22 | - Full C/C++ API in GDScript excluding lightuserdata/userdata 23 | - Pushing/Pulling normal Callables (lambda Callables aren't supported) 24 | - Pushing/Pulling Objects (Node / RefCounted) (as userdata) 25 | - Pushing/Pulling Godot Arrays and Dictionaries 26 | - Pushing/Pulling Godot Vector4s as Luau vectors 27 | - Custom print implementation that emits a signal 28 | - lua_loadstring, lua_dostring in GDScript (were "missing" in Luau) 29 | - Configurable interrupt signal for run-away scripts (`while true do end`) 30 | 31 | 32 | Compiling 33 | ------------ 34 | - Clone the repo with `--recursive` to also clone submodules 35 | - Run `scons platform=` (also, preferably, add `use_mingw=yes`, if you have it installed) 36 | - [A test project is included in the repo](https://github.com/Manonox/GDLuau/tree/master/demo) 37 | 38 | --------------- 39 | [![lua logo](https://luau-lang.org/assets/images/luau.png)](https://luau-lang.org)
40 | -------------------------------------------------------------------------------- /src/classes/luau_function.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | 9 | godot::Ref lua_tofunction(lua_State *L, int idx) { 10 | int ref = lua_ref(L, idx); 11 | return memnew(godot::LuauFunction(L, ref)); 12 | } 13 | 14 | using namespace godot; 15 | 16 | 17 | LuauFunction::LuauFunction(lua_State *L, int ref) { 18 | this->L = L; 19 | this->ref = ref; 20 | } 21 | 22 | LuauFunction::~LuauFunction() { 23 | lua_unref(L, this->ref); 24 | } 25 | 26 | Ref LuauFunction::pcall(const Variant **args, GDExtensionInt nargs, GDExtensionCallError &error) { 27 | lua_getref(L, ref); // Push lua function 28 | for (int i = 0; i < nargs; i++) { 29 | Variant arg = (*args)[i]; 30 | lua_pushvariant(L, arg); // Push arguments 31 | } 32 | 33 | return pcall_internal(nargs); 34 | } 35 | 36 | Ref LuauFunction::pcallv(const Array &args) { 37 | lua_getref(L, ref); // Push lua function 38 | int64_t nargs = args.size(); 39 | for (int64_t i = 0; i < nargs; i++) { 40 | lua_pushvariant(L, args[i]); // Push arguments 41 | } 42 | 43 | return pcall_internal(nargs); 44 | } 45 | 46 | Ref LuauFunction::pcall_internal(int nargs) { 47 | int result = lua_pcall(L, nargs, LUA_MULTRET, 0); 48 | 49 | if (result != LUA_OK) { 50 | const char *err = lua_tostring(L, -1); // Pull error message 51 | lua_pop(L, 1); // Pop error message 52 | return memnew(LuauFunctionResult(String(err))); 53 | } 54 | 55 | Array results; 56 | int nresults = lua_gettop(L); 57 | results.resize(nresults); 58 | for (int i = 0; i < nresults; i++) { 59 | results[i] = lua_tovariant(L, i + 1); 60 | } 61 | lua_settop(L, 0); // Clear stack 62 | 63 | return memnew(LuauFunctionResult(results)); 64 | } 65 | 66 | 67 | void LuauFunction::_bind_methods() { 68 | { 69 | MethodInfo mi; 70 | // mi.arguments.push_back(PropertyInfo(Variant::, "arguments")); 71 | mi.name = "pcall"; 72 | ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "pcall", &LuauFunction::pcall, mi); 73 | } 74 | 75 | ClassDB::bind_method(D_METHOD("pcallv", "args"), &LuauFunction::pcallv); 76 | } 77 | -------------------------------------------------------------------------------- /src/vector_lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | int lua_vector_lib_call(lua_State* L) { 6 | int nargs = lua_gettop(L); 7 | if (nargs > 5) { 8 | luaL_error(L, "vector5+ are not supported"); 9 | return 0; 10 | } 11 | 12 | if (nargs == 1) { 13 | lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f); 14 | return 1; 15 | } 16 | 17 | double x = luaL_checknumber(L, 2); 18 | double y = luaL_checknumber(L, 3); 19 | double z = 0.0; 20 | double w = 0.0; 21 | if (nargs >= 4) 22 | z = luaL_checknumber(L, 4); 23 | if (nargs == 5) 24 | w = luaL_checknumber(L, 5); 25 | 26 | lua_pushvector(L, float(x), float(y), float(z), float(w)); 27 | return 1; 28 | } 29 | 30 | int lua_vector_tostring(lua_State* L) { 31 | const float* v = luaL_checkvector(L, 1); 32 | lua_pushfstring(L, "%f, %f, %f, %f", v[0], v[1], v[2], v[3]); 33 | return 1; 34 | } 35 | 36 | int lua_vector_index(lua_State* L) { 37 | const float* v = luaL_checkvector(L, 1); 38 | int key = luaL_checkinteger(L, 2); 39 | 40 | if (key < 1 || key > 4) { 41 | luaL_error(L, "attempt to index vector with %s (1/2/3/4 expected)", lua_typename(L, lua_type(L, 2))); 42 | return 0; 43 | } 44 | 45 | lua_pushnumber(L, double(v[key-1])); 46 | return 1; 47 | } 48 | 49 | static const luaL_Reg veclib[] = { 50 | // hmm... 51 | {NULL, NULL}, 52 | }; 53 | 54 | int luaopen_vector(lua_State* L) { 55 | luaL_register(L, LUA_VECLIBNAME, veclib); 56 | luaL_newmetatable(L, "vector_lib"); 57 | 58 | lua_pushstring(L, "__call"); 59 | lua_pushcfunction(L, lua_vector_lib_call, nullptr); 60 | lua_rawset(L, -3); 61 | 62 | lua_setmetatable(L, -2); 63 | 64 | lua_pushvector(L, 0.0, 0.0, 0.0, 0.0); 65 | lua_setfield(L, -2, "zero"); 66 | lua_pushvector(L, -1.0, 0.0, 0.0, 0.0); 67 | lua_setfield(L, -2, "left"); 68 | lua_pushvector(L, 1.0, 0.0, 0.0, 0.0); 69 | lua_setfield(L, -2, "right"); 70 | lua_pushvector(L, 0.0, 1.0, 0.0, 0.0); 71 | lua_setfield(L, -2, "up"); 72 | lua_pushvector(L, 0.0, -1.0, 0.0, 0.0); 73 | lua_setfield(L, -2, "down"); 74 | lua_pushvector(L, 0.0, 0.0, -1.0, 0.0); 75 | lua_setfield(L, -2, "forward"); 76 | lua_pushvector(L, 0.0, 0.0, 1.0, 0.0); 77 | lua_setfield(L, -2, "back"); 78 | 79 | lua_pushvector(L, 0.0, 0.0, 0.0, 0.0); 80 | luaL_newmetatable(L, "vector"); 81 | 82 | lua_pushstring(L, "__tostring"); 83 | lua_pushcfunction(L, lua_vector_tostring, nullptr); 84 | lua_rawset(L, -3); 85 | 86 | lua_pushstring(L, "__index"); 87 | lua_pushcfunction(L, lua_vector_index, nullptr); 88 | lua_rawset(L, -3); 89 | 90 | lua_setmetatable(L, -2); 91 | lua_pop(L, 1); 92 | 93 | return 1; 94 | } 95 | -------------------------------------------------------------------------------- /demo/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/workflows/builds.yml: -------------------------------------------------------------------------------- 1 | name: Builds 2 | 3 | on: push 4 | 5 | env: 6 | LIBNAME: example 7 | 8 | concurrency: 9 | group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-macos 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{matrix.os}} 15 | name: ${{matrix.name}} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | include: 20 | - identifier: windows-debug 21 | os: windows-latest 22 | name: 🏁 Windows Debug 23 | target: template_debug 24 | platform: windows 25 | arch: x86_64 26 | - identifier: windows-release 27 | os: windows-latest 28 | name: 🏁 Windows Release 29 | target: template_release 30 | platform: windows 31 | arch: x86_64 32 | - identifier: macos-debug 33 | os: macos-latest 34 | name: 🍎 macOS (universal) Debug 35 | target: template_debug 36 | platform: macos 37 | arch: universal 38 | - identifier: macos-release 39 | os: macos-latest 40 | name: 🍎 macOS (universal) Release 41 | target: template_release 42 | platform: macos 43 | arch: universal 44 | - identifier: linux-debug 45 | os: ubuntu-latest 46 | name: 🐧 Linux Debug 47 | runner: ubuntu-20.04 48 | target: template_debug 49 | platform: linux 50 | arch: x86_64 51 | - identifier: linux-release 52 | os: ubuntu-latest 53 | name: 🐧 Linux Release 54 | runner: ubuntu-20.04 55 | target: template_release 56 | platform: linux 57 | arch: x86_64 58 | 59 | steps: 60 | - name: Checkout project 61 | uses: actions/checkout@v3 62 | with: 63 | submodules: recursive 64 | 65 | - name: Set up Python 66 | uses: actions/setup-python@v4 67 | with: 68 | python-version: '3.x' 69 | 70 | - name: Set up SCons 71 | shell: bash 72 | run: | 73 | python -c "import sys; print(sys.version)" 74 | python -m pip install scons 75 | scons --version 76 | - name: Linux dependencies 77 | if: ${{ matrix.platform == 'linux' }} 78 | run: | 79 | sudo apt-get update -qq 80 | sudo apt-get install -qqq build-essential pkg-config 81 | - name: Setup MinGW for Windows/MinGW build 82 | if: ${{ matrix.platform == 'windows' }} 83 | uses: egor-tensin/setup-mingw@v2 84 | with: 85 | version: 12.2.0 86 | 87 | - name: Compile godot-cpp 88 | shell: sh 89 | run: | 90 | scons target='${{ matrix.target }}' platform='${{ matrix.platform }}' arch='${{ matrix.arch }}' 91 | working-directory: godot-cpp 92 | 93 | - name: Compile Extension 94 | shell: sh 95 | run: | 96 | scons target='${{ matrix.target }}' platform='${{ matrix.platform }}' arch='${{ matrix.arch }}' 97 | - name: Delete compilation files 98 | if: ${{ matrix.platform == 'windows' }} 99 | run: | 100 | Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force 101 | - name: Upload artifact 102 | uses: actions/upload-artifact@v3 103 | with: 104 | name: ${{ github.event.repository.name }} 105 | path: | 106 | ${{ github.workspace }}/bin/* 107 | - name: Archive Release 108 | uses: thedoctor0/zip-release@0.7.1 109 | with: 110 | type: 'zip' 111 | filename: '${{ env.LIBNAME }}.${{ matrix.platform }}.${{ matrix.arch }}.zip' 112 | path: '${{ github.workspace }}/bin/' 113 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 114 | 115 | - name: Create and upload asset 116 | if: success() && github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 117 | uses: ncipollo/release-action@v1 118 | with: 119 | allowUpdates: true 120 | artifacts: "${{ env.LIBNAME }}.${{ matrix.platform }}.${{ matrix.arch }}.zip" 121 | omitNameDuringUpdate: true 122 | omitBodyDuringUpdate: true 123 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | 4 | 5 | def normalize_path(val, env): 6 | return val if os.path.isabs(val) else os.path.join(env.Dir("#").abspath, val) 7 | 8 | 9 | def validate_parent_dir(key, val, env): 10 | if not os.path.isdir(normalize_path(os.path.dirname(val), env)): 11 | raise UserError("'%s' is not a directory: %s" % (key, os.path.dirname(val))) 12 | 13 | 14 | libname = "gdluau" 15 | projectdir = "demo" 16 | 17 | localEnv = Environment(tools=["default"], PLATFORM="") 18 | 19 | customs = ["custom.py"] 20 | customs = [os.path.abspath(path) for path in customs] 21 | 22 | opts = Variables(customs, ARGUMENTS) 23 | opts.Add( 24 | BoolVariable( 25 | key="compiledb", 26 | help="Generate compilation DB (`compile_commands.json`) for external tools", 27 | default=localEnv.get("compiledb", False), 28 | ) 29 | ) 30 | opts.Add( 31 | PathVariable( 32 | key="compiledb_file", 33 | help="Path to a custom `compile_commands.json` file", 34 | default=localEnv.get("compiledb_file", "compile_commands.json"), 35 | validator=validate_parent_dir, 36 | ) 37 | ) 38 | opts.Update(localEnv) 39 | 40 | Help(opts.GenerateHelpText(localEnv)) 41 | 42 | env = localEnv.Clone() 43 | env["compiledb"] = False 44 | 45 | env.Tool("compilation_db") 46 | compilation_db = env.CompilationDatabase( 47 | normalize_path(localEnv["compiledb_file"], localEnv) 48 | ) 49 | env.Alias("compiledb", compilation_db) 50 | 51 | env = SConscript("godot-cpp/SConstruct", {"env": env, "customs": customs}) 52 | 53 | env.Append(CPPPATH=["src/", "/"]) 54 | sources = Glob("src/*.cpp") 55 | sources.append(Glob("src/classes/*.cpp")) 56 | 57 | file = "{}{}{}".format(libname, env["suffix"], env["SHLIBSUFFIX"]) 58 | 59 | 60 | 61 | ### > LUAU STUFF 62 | 63 | # lua_env = env.Clone() 64 | 65 | # # if env["target"] == "template_debug": 66 | # # lua_env.Append(CCFLAGS=["-g"]) 67 | 68 | # if lua_env["platform"] == "linux": 69 | # lua_env.Append(CPPDEFINES=["LUA_USE_POSIX"]) 70 | # elif lua_env["platform"] == "ios": 71 | # lua_env.Append(CPPDEFINES=["LUA_USE_IOS"]) 72 | 73 | # lua_env.Append(CPPDEFINES = ["MAKE_LIB"]) 74 | # # lua_env.Append(CXXFLAGS = ["-std=c++17"]) 75 | # lua_env.Append(CFLAGS = ["-std=c99"]) 76 | 77 | lua_cpp_paths = [] 78 | 79 | luau_paths = [ 80 | "Common", 81 | "Ast", 82 | "Compiler", 83 | "CodeGen", 84 | "Analysis", 85 | "Config", 86 | "VM", 87 | ] 88 | 89 | luau_include_paths = [os.path.join("luau", x, "include") for x in luau_paths] 90 | luau_source_paths = [os.path.join("luau", x, "src") for x in luau_paths] 91 | lua_cpp_paths.extend(luau_include_paths) 92 | lua_cpp_paths.extend(luau_source_paths) 93 | 94 | env.Append(CPPPATH=lua_cpp_paths) 95 | # env.AppendUnique(CPPPATH=lua_cpp_paths, delete_existing=True) 96 | 97 | 98 | lua_includes = [] 99 | lua_sources = [] 100 | for path in luau_include_paths: 101 | lua_includes.extend(Glob(path + "/*.hpp")) 102 | lua_includes.extend(Glob(path + "/*.h")) 103 | # lua_includes.extend(Glob(path + "/*.cpp")) 104 | # lua_includes.extend(Glob(path + "/*.c")) 105 | 106 | for path in luau_source_paths: 107 | # lua_sources.extend(Glob(path + "/*.hpp")) 108 | # lua_sources.extend(Glob(path + "/*.h")) 109 | lua_sources.extend(Glob(path + "/*.cpp")) 110 | lua_sources.extend(Glob(path + "/*.c")) 111 | 112 | sources.extend(lua_sources) 113 | 114 | # lua_file = "{}{}{}".format("luau", env["suffix"], env["SHLIBSUFFIX"]) 115 | # lua_libraryfile = "bin/{}/{}".format(env["platform"], lua_file) 116 | # lua_library = lua_env.StaticLibrary(lua_libraryfile, source=lua_sources) 117 | 118 | ### < LUAU STUFF 119 | 120 | 121 | 122 | 123 | if env["platform"] == "macos": 124 | platlibname = "{}.{}.{}".format(libname, env["platform"], env["target"]) 125 | file = "{}.framework/{}".format(env["platform"], platlibname, platlibname) 126 | 127 | libraryfile = "bin/{}/{}".format(env["platform"], file) 128 | 129 | # env.Append(CXXFLAGS=["-g"]) 130 | env.Append(CXXFLAGS=["-fexceptions"]) 131 | library = env.SharedLibrary( 132 | libraryfile, 133 | source=sources, 134 | ) 135 | 136 | copy = env.InstallAs("{}/addons/gdluau/bin/{}/{}".format(projectdir, env["platform"], file), library) 137 | 138 | default_args = [library, copy] 139 | if localEnv.get("compiledb", False): 140 | default_args += [compilation_db] 141 | Default(*default_args) 142 | -------------------------------------------------------------------------------- /src/classes/luau_vm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | enum lua_Lib { 14 | LUA_BASE_LIB = 0, 15 | LUA_COROUTINE_LIB, 16 | LUA_TABLE_LIB, 17 | LUA_OS_LIB, 18 | LUA_STRING_LIB, 19 | LUA_MATH_LIB, 20 | LUA_VECTOR_LIB, 21 | LUA_DEBUG_LIB, 22 | LUA_UTF8_LIB, 23 | LUA_BIT32_LIB 24 | }; 25 | 26 | 27 | namespace godot { 28 | 29 | class LuauVM : public Node { 30 | GDCLASS(LuauVM, Node) 31 | 32 | private: 33 | lua_State* L; 34 | 35 | void create_metatables(); 36 | 37 | protected: 38 | static void _bind_methods(); 39 | static void _bind_passthrough_methods(); 40 | 41 | public: 42 | LuauVM(); 43 | ~LuauVM(); 44 | 45 | double interrupt_cooldown = 0.1; 46 | std::chrono::system_clock::time_point last_interrupt_time; 47 | 48 | void set_interrupt_cooldown(const double p_interrupt_cooldown); 49 | double get_interrupt_cooldown(); 50 | 51 | void register_print(); 52 | 53 | int load_string(const String &code, const String &chunkname); 54 | int do_string(const String &code, const String &chunkname); 55 | 56 | void open_libraries(const PackedByteArray &libraries); 57 | void open_all_libraries(); 58 | 59 | int64_t get_memory_usage_bytes(); 60 | 61 | 62 | // Bindings 63 | 64 | #pragma region GDLuau 65 | 66 | void lua_pushvariant(const Variant &var); 67 | void lua_pusharray(const Array &array); 68 | void lua_pushdictionary(const Dictionary &dictionary); 69 | 70 | Variant lua_tovariant(int index); 71 | Array lua_toarray(int index); 72 | Dictionary lua_todictionary(int index); 73 | 74 | Ref lua_tofunction(int index); 75 | Error lua_pushcallable(const Callable &callable, const String &debugname); 76 | 77 | void lua_pushobject(Object *node); 78 | Object *lua_toobject(int idx); 79 | bool lua_isobject(int idx); 80 | 81 | #pragma endregion 82 | 83 | #pragma region Default 84 | void lua_setreadonly(int index, bool enabled); 85 | 86 | void lua_call(int nargs, int nresults); 87 | void lua_concat(int n); 88 | void lua_createtable(int narr, int nrec); 89 | void lua_equal(int index1, int index2); 90 | void lua_error(); 91 | void lua_getfenv(int index); 92 | int lua_getfield(int index, const String &key); 93 | void (lua_getglobal)(const String &key); 94 | bool lua_getmetatable(int index); 95 | int lua_gettable(int index); 96 | int lua_gettop(); 97 | void lua_insert(int index); 98 | bool (lua_isboolean)(int index); 99 | bool (lua_isfunction)(int index); 100 | bool (lua_isnil)(int index); 101 | bool (lua_isnone)(int index); 102 | bool (lua_isnoneornil)(int index); 103 | bool lua_isnumber(int index); 104 | bool lua_isinteger(int index); 105 | bool(lua_isnumberx)(int index); 106 | bool lua_isstring(int index); 107 | bool (lua_isvector)(int index); 108 | bool (lua_istable)(int index); 109 | bool (lua_isthread)(int index); 110 | bool lua_isuserdata(int index); 111 | bool lua_lessthan(int index1, int index2); 112 | void (lua_newtable)(); 113 | void (lua_newuserdata)(int size); 114 | bool lua_next(int index); 115 | int64_t lua_objlen(int index); 116 | int lua_pcall(int nargs, int nresults, int errfunc); 117 | void (lua_pop)(int n); 118 | void lua_pushnil(); 119 | void lua_pushboolean(bool b); 120 | void lua_pushinteger(int x); 121 | void lua_pushnumber(float x); 122 | void lua_pushstring(const String &s); 123 | #if LUA_VECTOR_SIZE == 4 124 | void lua_pushvector(Vector4 v); 125 | #else 126 | void lua_pushvector(Vector3 v); 127 | #endif 128 | void lua_pushvalue(int index); 129 | bool lua_rawequal(int index1, int index2); 130 | void lua_rawget(int index); 131 | void lua_rawgeti(int index, int n); 132 | void lua_rawgetfield(int index, const String &key); 133 | void lua_rawset(int index); 134 | void lua_rawseti(int index, int n); 135 | void lua_rawsetfield(int index, const String &key); 136 | void lua_remove(int index); 137 | void lua_replace(int index); 138 | bool lua_setfenv(int index); 139 | void lua_setfield(int index, const String &key); 140 | void (lua_setglobal)(const String &key); 141 | bool lua_setmetatable(int index); 142 | void lua_settable(int index); 143 | void lua_settop(int index); 144 | 145 | int lua_status(); 146 | bool lua_toboolean(int index); 147 | int64_t (lua_tointeger)(int index); 148 | double (lua_tonumber)(int index); 149 | String (lua_tostring)(int index); 150 | #if LUA_VECTOR_SIZE == 4 151 | Vector4 lua_tovector(int index); 152 | #else 153 | Vector3 lua_tovector(int index); 154 | #endif 155 | int lua_type(int index); 156 | String lua_typename(int type); 157 | String lua_getupvalue(int index, int n); 158 | String lua_setupvalue(int index, int n); 159 | int lua_gc(int what, int data); 160 | 161 | int lua_ref(int t); 162 | void lua_unref(int ref); 163 | int (lua_getref)(int ref); 164 | 165 | #pragma endregion 166 | 167 | #pragma region Auxiliary 168 | 169 | bool luaL_hasmetatable(int index, const String &tname); 170 | 171 | void(luaL_error)(const String &err); 172 | bool luaL_callmeta(int obj, const String &field); 173 | bool luaL_getmetafield(int obj, const String &field); 174 | bool luaL_newmetatable(const String &tname); 175 | bool (luaL_getmetatable)(const String &tname); 176 | void luaL_where(int lvl); 177 | void luaL_typerror(int nargs, const String &tname); 178 | void (luaL_argcheck)(bool cond, int narg, const String &msg); 179 | 180 | void luaL_checkany(int narg); 181 | int luaL_checkint(int narg); 182 | double luaL_checknumber(int narg); 183 | String(luaL_checkstring)(int narg); 184 | #if LUA_VECTOR_SIZE == 4 185 | Vector4 luaL_checkvector(int narg); 186 | #else 187 | Vector3 luaL_checkvector(int narg); 188 | #endif 189 | bool lua_isvalidobject(int index); 190 | Object *luaL_checkobject(int narg, bool valid); 191 | void luaL_checktype(int narg, int type); 192 | void luaL_checkstack(int sz, const String &messsage); 193 | int luaL_checkoption(int narg, const Array &array, const String &def); 194 | 195 | #pragma endregion 196 | }; 197 | } 198 | 199 | 200 | VARIANT_ENUM_CAST(lua_Status); 201 | VARIANT_ENUM_CAST(lua_Type); 202 | VARIANT_ENUM_CAST(lua_Lib); 203 | VARIANT_ENUM_CAST(lua_GCOp); 204 | 205 | int luaopen_vector(lua_State *L); 206 | 207 | 208 | #include 209 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Commented out parameters are those with the same value as base LLVM style. 2 | # We can uncomment them if we want to change their value, or enforce the 3 | # chosen value in case the base style changes (last sync: Clang 14.0). 4 | --- 5 | ### General config, applies to all languages ### 6 | BasedOnStyle: LLVM 7 | AccessModifierOffset: -4 8 | AlignAfterOpenBracket: DontAlign 9 | # AlignArrayOfStructures: None 10 | # AlignConsecutiveMacros: None 11 | # AlignConsecutiveAssignments: None 12 | # AlignConsecutiveBitFields: None 13 | # AlignConsecutiveDeclarations: None 14 | # AlignEscapedNewlines: Right 15 | AlignOperands: DontAlign 16 | AlignTrailingComments: false 17 | # AllowAllArgumentsOnNextLine: true 18 | AllowAllParametersOfDeclarationOnNextLine: false 19 | # AllowShortEnumsOnASingleLine: true 20 | # AllowShortBlocksOnASingleLine: Never 21 | # AllowShortCaseLabelsOnASingleLine: false 22 | # AllowShortFunctionsOnASingleLine: All 23 | # AllowShortLambdasOnASingleLine: All 24 | # AllowShortIfStatementsOnASingleLine: Never 25 | # AllowShortLoopsOnASingleLine: false 26 | # AlwaysBreakAfterDefinitionReturnType: None 27 | # AlwaysBreakAfterReturnType: None 28 | # AlwaysBreakBeforeMultilineStrings: false 29 | # AlwaysBreakTemplateDeclarations: MultiLine 30 | # AttributeMacros: 31 | # - __capability 32 | # BinPackArguments: true 33 | # BinPackParameters: true 34 | # BraceWrapping: 35 | # AfterCaseLabel: false 36 | # AfterClass: false 37 | # AfterControlStatement: Never 38 | # AfterEnum: false 39 | # AfterFunction: false 40 | # AfterNamespace: false 41 | # AfterObjCDeclaration: false 42 | # AfterStruct: false 43 | # AfterUnion: false 44 | # AfterExternBlock: false 45 | # BeforeCatch: false 46 | # BeforeElse: false 47 | # BeforeLambdaBody: false 48 | # BeforeWhile: false 49 | # IndentBraces: false 50 | # SplitEmptyFunction: true 51 | # SplitEmptyRecord: true 52 | # SplitEmptyNamespace: true 53 | # BreakBeforeBinaryOperators: None 54 | # BreakBeforeConceptDeclarations: true 55 | # BreakBeforeBraces: Attach 56 | # BreakBeforeInheritanceComma: false 57 | # BreakInheritanceList: BeforeColon 58 | # BreakBeforeTernaryOperators: true 59 | # BreakConstructorInitializersBeforeComma: false 60 | BreakConstructorInitializers: AfterColon 61 | # BreakStringLiterals: true 62 | ColumnLimit: 0 63 | # CommentPragmas: '^ IWYU pragma:' 64 | # QualifierAlignment: Leave 65 | # CompactNamespaces: false 66 | ConstructorInitializerIndentWidth: 8 67 | ContinuationIndentWidth: 8 68 | Cpp11BracedListStyle: false 69 | # DeriveLineEnding: true 70 | # DerivePointerAlignment: false 71 | # DisableFormat: false 72 | # EmptyLineAfterAccessModifier: Never 73 | # EmptyLineBeforeAccessModifier: LogicalBlock 74 | # ExperimentalAutoDetectBinPacking: false 75 | # PackConstructorInitializers: BinPack 76 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 77 | # AllowAllConstructorInitializersOnNextLine: true 78 | # FixNamespaceComments: true 79 | # ForEachMacros: 80 | # - foreach 81 | # - Q_FOREACH 82 | # - BOOST_FOREACH 83 | # IfMacros: 84 | # - KJ_IF_MAYBE 85 | # IncludeBlocks: Preserve 86 | IncludeCategories: 87 | - Regex: '".*"' 88 | Priority: 1 89 | - Regex: '^<.*\.h>' 90 | Priority: 2 91 | - Regex: '^<.*' 92 | Priority: 3 93 | # IncludeIsMainRegex: '(Test)?$' 94 | # IncludeIsMainSourceRegex: '' 95 | # IndentAccessModifiers: false 96 | IndentCaseLabels: true 97 | # IndentCaseBlocks: false 98 | # IndentGotoLabels: true 99 | # IndentPPDirectives: None 100 | # IndentExternBlock: AfterExternBlock 101 | # IndentRequires: false 102 | IndentWidth: 4 103 | # IndentWrappedFunctionNames: false 104 | # InsertTrailingCommas: None 105 | # JavaScriptQuotes: Leave 106 | # JavaScriptWrapImports: true 107 | KeepEmptyLinesAtTheStartOfBlocks: false 108 | # LambdaBodyIndentation: Signature 109 | # MacroBlockBegin: '' 110 | # MacroBlockEnd: '' 111 | # MaxEmptyLinesToKeep: 1 112 | # NamespaceIndentation: None 113 | # PenaltyBreakAssignment: 2 114 | # PenaltyBreakBeforeFirstCallParameter: 19 115 | # PenaltyBreakComment: 300 116 | # PenaltyBreakFirstLessLess: 120 117 | # PenaltyBreakOpenParenthesis: 0 118 | # PenaltyBreakString: 1000 119 | # PenaltyBreakTemplateDeclaration: 10 120 | # PenaltyExcessCharacter: 1000000 121 | # PenaltyReturnTypeOnItsOwnLine: 60 122 | # PenaltyIndentedWhitespace: 0 123 | # PointerAlignment: Right 124 | # PPIndentWidth: -1 125 | # ReferenceAlignment: Pointer 126 | # ReflowComments: true 127 | # RemoveBracesLLVM: false 128 | # SeparateDefinitionBlocks: Leave 129 | # ShortNamespaceLines: 1 130 | # SortIncludes: CaseSensitive 131 | # SortJavaStaticImport: Before 132 | # SortUsingDeclarations: true 133 | # SpaceAfterCStyleCast: false 134 | # SpaceAfterLogicalNot: false 135 | # SpaceAfterTemplateKeyword: true 136 | # SpaceBeforeAssignmentOperators: true 137 | # SpaceBeforeCaseColon: false 138 | # SpaceBeforeCpp11BracedList: false 139 | # SpaceBeforeCtorInitializerColon: true 140 | # SpaceBeforeInheritanceColon: true 141 | # SpaceBeforeParens: ControlStatements 142 | # SpaceBeforeParensOptions: 143 | # AfterControlStatements: true 144 | # AfterForeachMacros: true 145 | # AfterFunctionDefinitionName: false 146 | # AfterFunctionDeclarationName: false 147 | # AfterIfMacros: true 148 | # AfterOverloadedOperator: false 149 | # BeforeNonEmptyParentheses: false 150 | # SpaceAroundPointerQualifiers: Default 151 | # SpaceBeforeRangeBasedForLoopColon: true 152 | # SpaceInEmptyBlock: false 153 | # SpaceInEmptyParentheses: false 154 | # SpacesBeforeTrailingComments: 1 155 | # SpacesInAngles: Never 156 | # SpacesInConditionalStatement: false 157 | # SpacesInContainerLiterals: true 158 | # SpacesInCStyleCastParentheses: false 159 | ## Godot TODO: We'll want to use a min of 1, but we need to see how to fix 160 | ## our comment capitalization at the same time. 161 | SpacesInLineCommentPrefix: 162 | Minimum: 0 163 | Maximum: -1 164 | # SpacesInParentheses: false 165 | # SpacesInSquareBrackets: false 166 | # SpaceBeforeSquareBrackets: false 167 | # BitFieldColonSpacing: Both 168 | # StatementAttributeLikeMacros: 169 | # - Q_EMIT 170 | # StatementMacros: 171 | # - Q_UNUSED 172 | # - QT_REQUIRE_VERSION 173 | TabWidth: 4 174 | # UseCRLF: false 175 | UseTab: Always 176 | # WhitespaceSensitiveMacros: 177 | # - STRINGIZE 178 | # - PP_STRINGIZE 179 | # - BOOST_PP_STRINGIZE 180 | # - NS_SWIFT_NAME 181 | # - CF_SWIFT_NAME 182 | --- 183 | ### C++ specific config ### 184 | Language: Cpp 185 | Standard: c++17 186 | --- 187 | ### ObjC specific config ### 188 | Language: ObjC 189 | # ObjCBinPackProtocolList: Auto 190 | ObjCBlockIndentWidth: 4 191 | # ObjCBreakBeforeNestedBlockParam: true 192 | # ObjCSpaceAfterProperty: false 193 | # ObjCSpaceBeforeProtocolList: true 194 | --- 195 | ### Java specific config ### 196 | Language: Java 197 | # BreakAfterJavaFieldAnnotations: false 198 | JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax'] 199 | ... 200 | -------------------------------------------------------------------------------- /src/classes/luau_vm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | using namespace godot; 12 | 13 | 14 | // Source: https://github.com/Fumohouse/godot-luau-script 15 | // Based on the default implementation seen in the Lua 5.1 reference 16 | static void *lua_alloc(void *, void *ptr, size_t, size_t nsize) { 17 | if (nsize == 0) { 18 | // Lua assumes free(NULL) is ok. For Godot it is not. 19 | if (ptr) 20 | memfree(ptr); 21 | 22 | return nullptr; 23 | } 24 | 25 | return memrealloc(ptr, nsize); 26 | } 27 | 28 | static lua_CompileOptions luau_vm_compile_options = { 29 | // int optimizationLevel; 30 | 1, 31 | 32 | // int debugLevel; 33 | 1, 34 | 35 | // int typeInfoLevel; 36 | 0, 37 | 38 | // int coverageLevel; 39 | 0, 40 | 41 | // const char* vectorLib; 42 | nullptr, 43 | 44 | // const char* vectorCtor; 45 | "vector", 46 | 47 | // const char** mutableGlobals; 48 | }; 49 | 50 | using ms = std::chrono::duration; 51 | void luau_vm_interrupt_method(lua_State* L, int gc) { 52 | LuauVM *node = lua_getnode(L); 53 | double cooldown = node->interrupt_cooldown; 54 | auto now = std::chrono::system_clock::now(); 55 | 56 | double time = ms(now - node->last_interrupt_time).count() / 1000.0; 57 | if (time < cooldown) return; 58 | node->last_interrupt_time = now; 59 | node->emit_signal("interrupt"); 60 | } 61 | 62 | 63 | LuauVM::LuauVM() { 64 | L = lua_newstate(lua_alloc, nullptr); 65 | lua_setnode(L, this); 66 | create_metatables(); 67 | 68 | lua_callbacks(L)->interrupt = luau_vm_interrupt_method; 69 | } 70 | 71 | void LuauVM::set_interrupt_cooldown(const double p_interrupt_cooldown) { interrupt_cooldown = p_interrupt_cooldown; } 72 | double LuauVM::get_interrupt_cooldown() { return interrupt_cooldown; } 73 | 74 | 75 | LuauVM::~LuauVM() { 76 | lua_close(L); 77 | } 78 | 79 | void LuauVM::_bind_methods() { 80 | ClassDB::bind_method(D_METHOD("get_interrupt_cooldown"), &LuauVM::get_interrupt_cooldown); 81 | ClassDB::bind_method(D_METHOD("set_interrupt_cooldown", "p_interrupt_cooldown"), &LuauVM::set_interrupt_cooldown); 82 | ClassDB::add_property("LuauVM", PropertyInfo(Variant::FLOAT, "interrupt_cooldown"), "set_interrupt_cooldown", "get_interrupt_cooldown"); 83 | 84 | ClassDB::bind_method(D_METHOD("load_string", "code", "chunkname"), &LuauVM::load_string, DEFVAL("loadstring")); 85 | ClassDB::bind_method(D_METHOD("do_string", "code", "chunkname"), &LuauVM::do_string, DEFVAL("dostring")); 86 | 87 | ClassDB::bind_method(D_METHOD("open_libraries", "libraries"), &LuauVM::open_libraries); 88 | ClassDB::bind_method(D_METHOD("open_all_libraries"), &LuauVM::open_all_libraries); 89 | 90 | _bind_passthrough_methods(); 91 | 92 | ADD_SIGNAL(MethodInfo("stdout", PropertyInfo(Variant::STRING, "message"))); 93 | ADD_SIGNAL(MethodInfo("interrupt")); 94 | } 95 | 96 | int metatable_object__eq(lua_State *L) { 97 | Variant var1 = lua_toobject(L, 1); 98 | Variant var2 = lua_toobject(L, 2); 99 | if (var1.get_type() != Variant::Type::OBJECT && var2.get_type() != Variant::Type::OBJECT) { 100 | ::lua_pushboolean(L, ::lua_equal(L, 1, 2)); 101 | return 1; 102 | } 103 | godot::Object *object1 = var1.operator godot::Object *(); 104 | godot::Object *object2 = var2.operator godot::Object *(); 105 | ::lua_pushboolean(L, object1->get_instance_id() == object2->get_instance_id()); 106 | return 1; 107 | } 108 | 109 | void LuauVM::create_metatables() { 110 | luaL_newmetatable("object"); 111 | 112 | lua_setuserdatadtor(L, 1, object_userdata_dtor); 113 | 114 | ::lua_pushstring(L, "object"); 115 | ::lua_rawsetfield(L, -2, "__type"); 116 | 117 | ::lua_pushcfunction(L, metatable_object__eq, NULL); 118 | ::lua_rawsetfield(L, -2, "__eq"); 119 | 120 | (lua_pop)(1); 121 | } 122 | 123 | 124 | 125 | static int godot_print(lua_State* L) { 126 | LuauVM *node = lua_getnode(L); 127 | int nargs = node->lua_gettop(); 128 | 129 | String s = String(); 130 | for (int i = 1; i <= nargs; i++) { 131 | String ss; 132 | if (node->lua_isnumber(i) || node->lua_isstring(i)) 133 | ss = (node->lua_tostring)(i); 134 | else { 135 | (node->lua_getglobal)("tostring"); // Push global "tostring" 136 | node->lua_pushvalue(i); // Push argument 137 | 138 | int err = node->lua_pcall(1, 1, 0); // Call tostring 139 | 140 | if (err != LUA_OK) { 141 | lua_error(L); 142 | return 0; 143 | } 144 | 145 | ss = (node->lua_tostring)(-1); 146 | (node->lua_pop)(1); // Pop tostring'ed argument 147 | } 148 | 149 | s += ss; 150 | if (i < nargs) s += '\t'; 151 | } 152 | 153 | node->emit_signal("stdout", s); 154 | return 0; 155 | } 156 | 157 | 158 | int lua_loadstring(lua_State* L) 159 | { 160 | size_t l = 0; 161 | const char* s = luaL_checklstring(L, 1, &l); 162 | const char* chunkname = luaL_optstring(L, 2, s); 163 | 164 | lua_setsafeenv(L, LUA_ENVIRONINDEX, false); 165 | 166 | size_t bytecode_size = 0; 167 | char *bytecode = luau_compile(s, l, &luau_vm_compile_options, &bytecode_size); 168 | if (luau_load(L, chunkname, bytecode, bytecode_size, 0) == 0) 169 | return 1; 170 | 171 | lua_pushnil(L); 172 | lua_insert(L, -2); // put before error message 173 | return 2; // return nil plus error message 174 | } 175 | 176 | 177 | int luaopen_base_luau(lua_State *L) { 178 | int nret = luaopen_base(L); 179 | ::lua_pushcfunction(L, godot_print, "print"); 180 | ::lua_rawsetfield(L, LUA_GLOBALSINDEX, "print"); 181 | ::lua_pushcfunction(L, lua_loadstring, "loadstring"); 182 | ::lua_rawsetfield(L, LUA_GLOBALSINDEX, "loadstring"); 183 | return nret; 184 | } 185 | 186 | 187 | static const uint8_t lualibsLength = 10; 188 | static const luaL_Reg lualibs[] = { 189 | {"", luaopen_base_luau}, 190 | {LUA_COLIBNAME, luaopen_coroutine}, 191 | {LUA_TABLIBNAME, luaopen_table}, 192 | {LUA_OSLIBNAME, luaopen_os}, 193 | {LUA_STRLIBNAME, luaopen_string}, 194 | {LUA_MATHLIBNAME, luaopen_math}, 195 | {LUA_VECLIBNAME, luaopen_vector}, 196 | {LUA_DBLIBNAME, luaopen_debug}, 197 | {LUA_UTF8LIBNAME, luaopen_utf8}, 198 | {LUA_BITLIBNAME, luaopen_bit32}, 199 | }; 200 | 201 | 202 | void LuauVM::open_libraries(const PackedByteArray &libraries) { 203 | for (uint8_t i = 0; i < libraries.size(); ++i) { 204 | uint8_t index = libraries[i]; 205 | if (index >= lualibsLength) 206 | continue; 207 | lua_pushcfunction(L, lualibs[index].func, NULL); 208 | lua_pushstring(lualibs[index].name); 209 | lua_call(1, 0); 210 | } 211 | } 212 | 213 | void LuauVM::open_all_libraries() { 214 | for (uint8_t i = 0; i < lualibsLength; ++i) { 215 | lua_pushcfunction(L, lualibs[i].func, NULL); 216 | lua_pushstring(lualibs[i].name); 217 | lua_call(1, 0); 218 | } 219 | } 220 | 221 | 222 | int64_t LuauVM::get_memory_usage_bytes() { 223 | return lua_gc(LUA_GCCOUNTB, 0) + 1024 * lua_gc(LUA_GCCOUNT, 0); 224 | } 225 | 226 | 227 | int LuauVM::load_string(const String &code, const String &chunkname) { 228 | auto utf8 = code.ascii(); 229 | auto source = utf8.get_data(); 230 | size_t bytecode_size = 0; 231 | char *bytecode = luau_compile(source, strlen(source), &luau_vm_compile_options, &bytecode_size); 232 | if (bytecode == nullptr) 233 | return -1; 234 | 235 | int status = luau_load(L, chunkname.ascii().get_data(), bytecode, bytecode_size, 0); 236 | std::free(bytecode); 237 | return status; 238 | } 239 | 240 | int LuauVM::do_string(const String &code, const String &chunkname) { 241 | int status = load_string(code, chunkname); 242 | if (status != LUA_OK) 243 | return status; 244 | status = lua_pcall(0, LUA_MULTRET, 0); 245 | return status; 246 | } 247 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | // #include 5 | 6 | 7 | 8 | void lua_pushvariant(lua_State *L, const godot::Variant &var) { 9 | switch (var.get_type()) 10 | { 11 | case godot::Variant::Type::NIL: 12 | lua_pushnil(L); 13 | break; 14 | case godot::Variant::Type::STRING: 15 | lua_pushstring(L, (var.operator godot::String()).ascii().get_data()); 16 | break; 17 | case godot::Variant::Type::INT: 18 | lua_pushinteger(L, var.operator int32_t()); 19 | break; 20 | case godot::Variant::Type::FLOAT: 21 | lua_pushnumber(L, var.operator double()); 22 | break; 23 | case godot::Variant::Type::BOOL: 24 | lua_pushboolean(L, var.operator bool()); 25 | break; 26 | case godot::Variant::Type::ARRAY: { 27 | lua_pusharray(L, var.operator godot::Array()); 28 | break; 29 | } 30 | case godot::Variant::Type::DICTIONARY: { 31 | lua_pushdictionary(L, var.operator godot::Dictionary()); 32 | break; 33 | } 34 | 35 | case godot::Variant::Type::VECTOR2: { 36 | godot::Vector2 v = var.operator godot::Vector2(); 37 | lua_pushvector(L, v.x, v.y, 0.0f, 0.0f); 38 | break; 39 | } 40 | 41 | case godot::Variant::Type::VECTOR2I: { 42 | godot::Vector2i v = var.operator godot::Vector2i(); 43 | lua_pushvector(L, (float)v.x, (float)v.y, 0.0f, 0.0f); 44 | break; 45 | } 46 | 47 | case godot::Variant::Type::VECTOR3: { 48 | godot::Vector3 v = var.operator godot::Vector3(); 49 | lua_pushvector(L, v.x, v.y, v.z, 0.0f); 50 | break; 51 | } 52 | 53 | case godot::Variant::Type::VECTOR3I: { 54 | godot::Vector3i v = var.operator godot::Vector3i(); 55 | lua_pushvector(L, v.x, v.y, v.z, 0.0f); 56 | break; 57 | } 58 | 59 | case godot::Variant::Type::CALLABLE: { 60 | godot::Callable c = var.operator godot::Callable(); 61 | lua_pushcallable(L, c, godot::String()); 62 | break; 63 | } 64 | 65 | case godot::Variant::Type::OBJECT: { 66 | godot::Object *o = var.operator godot::Object *(); 67 | lua_pushobject(L, o); 68 | break; 69 | } 70 | 71 | default: { 72 | lua_pushnil(L); 73 | break; 74 | } 75 | } 76 | } 77 | 78 | void lua_pusharray(lua_State *L, const godot::Array &array) { 79 | lua_createtable(L, array.size(), 0); 80 | 81 | for (int i = 0; i < array.size(); i++) { 82 | godot::Variant value = array[i]; 83 | 84 | lua_pushinteger(L, i + 1); 85 | lua_pushvariant(L, value); 86 | lua_rawset(L, -3); 87 | } 88 | } 89 | 90 | void lua_pushdictionary(lua_State *L, const godot::Dictionary &dict) { 91 | lua_createtable(L, 0, dict.size()); 92 | 93 | auto keys = dict.keys(); 94 | for(int i = 0; i < dict.size(); i++) { 95 | godot::Variant key = keys[i]; 96 | godot::Variant value = dict[key]; 97 | 98 | lua_pushvariant(L, key); 99 | if ((key.get_type() == godot::Variant::STRING || key.get_type() == godot::Variant::STRING_NAME) && value.get_type() == godot::Variant::CALLABLE) { 100 | lua_pushcallable(L, value.operator godot::Callable(), key.operator godot::String()); 101 | } else { 102 | lua_pushvariant(L, value); 103 | } 104 | lua_rawset(L, -3); 105 | } 106 | } 107 | 108 | 109 | godot::Variant lua_tovariant(lua_State *L, int idx) { 110 | int type = lua_type(L, idx); 111 | 112 | switch (type) { 113 | case LUA_TNIL: { 114 | return godot::Variant(); 115 | } 116 | 117 | case LUA_TBOOLEAN: { 118 | return (bool)lua_toboolean(L, idx); 119 | } 120 | 121 | case LUA_TNUMBER: { 122 | return lua_tonumber(L, idx); 123 | } 124 | 125 | case LUA_TVECTOR: { 126 | const float* vec = lua_tovector(L, idx); 127 | return godot::Vector3(vec[0], vec[1], vec[2]); 128 | } 129 | 130 | case LUA_TSTRING: { 131 | const char* s = lua_tostring(L, idx); 132 | return godot::String(s); 133 | } 134 | 135 | case LUA_TTABLE: { 136 | if (luaL_isarray(L, idx)) 137 | return lua_toarray(L, idx); 138 | return lua_todictionary(L, idx); 139 | } 140 | 141 | case LUA_TFUNCTION: { 142 | return lua_tofunction(L, idx); 143 | } 144 | 145 | case LUA_TUSERDATA: { 146 | if (lua_isobject(L, idx)) { 147 | return lua_toobject(L, idx); 148 | } 149 | 150 | return godot::Variant(); 151 | } 152 | 153 | default: { 154 | return godot::Variant(); 155 | } 156 | } 157 | } 158 | 159 | 160 | godot::Variant lua_toarray(lua_State *L, int idx) { 161 | if (!lua_istable(L, idx)) return godot::Variant(); 162 | size_t length = lua_objlen(L, idx); 163 | godot::Array array; 164 | for (int i = 1; i <= length; i++) { 165 | lua_rawgeti(L, idx, i); 166 | array.push_back(lua_tovariant(L, -1)); 167 | lua_pop(L, 1); 168 | } 169 | return array; 170 | } 171 | 172 | godot::Variant lua_todictionary(lua_State *L, int idx) { 173 | if (!lua_istable(L, idx)) return godot::Variant(); 174 | godot::Dictionary dict; 175 | lua_pushvalue(L, idx); 176 | lua_pushnil(L); 177 | while (lua_next(L, -2) != 0) { 178 | godot::Variant key = lua_tovariant(L, -2); 179 | godot::Variant value = lua_tovariant(L, -1); 180 | dict[key] = value; 181 | lua_pop(L, 1); 182 | } 183 | lua_pop(L, 1); 184 | return dict; 185 | } 186 | 187 | 188 | // Might be faulty and/or unneeded 189 | bool luaL_isarray(lua_State *L, int idx) { 190 | lua_pushvalue(L, idx); 191 | lua_pushnil(L); 192 | int has_keys = lua_next(L, -2); 193 | if (!has_keys) { 194 | lua_pop(L, 1); 195 | return false; 196 | } 197 | 198 | if (lua_type(L, -2) != LUA_TNUMBER) { 199 | lua_pop(L, 3); 200 | return false; 201 | } 202 | 203 | lua_pop(L, 2); 204 | 205 | size_t size = lua_objlen(L, -1); 206 | lua_pushinteger(L, (int)size); 207 | int has_more = lua_next(L, -2); 208 | 209 | if (!has_more) { 210 | lua_pop(L, 1); 211 | return true; 212 | } 213 | 214 | lua_pop(L, 3); 215 | return false; 216 | } 217 | 218 | 219 | typedef struct CallableWrapped { 220 | int64_t object_id; 221 | char method[257]; 222 | char debugname[129]; 223 | } CallableWrapped; 224 | 225 | 226 | // static godot::Array arr; 227 | int lua_pushcallable_method(lua_State *L) { 228 | CallableWrapped *p_callablewrapped = (CallableWrapped *)lua_touserdata(L, lua_upvalueindex(1)); 229 | 230 | if (!godot::UtilityFunctions::is_instance_id_valid(p_callablewrapped->object_id)) { 231 | luaL_error(L, "attempt to call method on an invalid object"); 232 | return 0; 233 | } 234 | 235 | godot::Object *object_p = godot::ObjectDB::get_instance(p_callablewrapped->object_id); 236 | 237 | if (!object_p->has_method(p_callablewrapped->method)) { 238 | luaL_error(L, "attempt to call nonexistent external method"); 239 | return 0; 240 | } 241 | 242 | godot::LuauVM* vm = lua_getnode(L); 243 | 244 | // arr.clear(); 245 | // arr.push_back(vm); 246 | int nresults = object_p->call(p_callablewrapped->method, vm); 247 | // int nresults = object_p->callv(p_callablewrapped->method, arr); // Work-around, can't use 'call' for some reason 248 | return nresults; 249 | } 250 | 251 | void lua_pushcallable(lua_State *L, const godot::Callable &callable, const godot::String &debugname) { 252 | if (callable.is_custom()) { 253 | godot::UtilityFunctions::push_warning("[GDLUAU] Custom Callables not implemented"); 254 | lua_pushnil(L); 255 | return; 256 | } 257 | 258 | if (callable.is_null()) { 259 | godot::UtilityFunctions::push_warning("[GDLUAU] Trying push invalid Callable"); 260 | lua_pushnil(L); 261 | return; 262 | } 263 | 264 | CallableWrapped *p_callablewrapped = (CallableWrapped *)lua_newuserdata(L, sizeof(CallableWrapped)); 265 | p_callablewrapped->object_id = callable.get_object_id(); 266 | 267 | { 268 | auto method = (godot::String) callable.get_method(); 269 | 270 | const int max_length = 257; 271 | auto length = method.ascii().length(); 272 | if (length > max_length) { 273 | length = max_length; 274 | godot::UtilityFunctions::push_error("Too long method name"); 275 | } 276 | 277 | auto src = method.to_ascii_buffer(); 278 | 279 | for (int i = 0; i < length; i++) 280 | p_callablewrapped->method[i] = src[i]; 281 | p_callablewrapped->method[length] = '\0'; 282 | } 283 | 284 | { 285 | const int max_length = 129; 286 | auto length = debugname.ascii().length(); 287 | length = length > max_length ? max_length : length; 288 | auto src = debugname.to_ascii_buffer(); 289 | 290 | for (int i = 0; i < length; i++) 291 | p_callablewrapped->debugname[i] = src[i]; 292 | p_callablewrapped->debugname[length] = '\0'; 293 | } 294 | 295 | lua_pushcclosure(L, lua_pushcallable_method, p_callablewrapped->debugname, 1); 296 | } 297 | 298 | 299 | bool luaL_hasmetatable(lua_State* L, int idx, const char* tname) { 300 | if (!lua_getmetatable(L, idx)) 301 | return false; 302 | luaL_getmetatable(L, tname); 303 | bool result = lua_rawequal(L, -2, -1); 304 | lua_pop(L, 2); 305 | return result; 306 | } 307 | 308 | 309 | typedef struct ObjectWrapped { 310 | int64_t object_id; 311 | } ObjectWrapped; 312 | 313 | void lua_pushobject(lua_State *L, godot::Object *object) { 314 | int tag = 0; 315 | if (object->is_class("RefCounted")) { 316 | godot::RefCounted *refcounted = reinterpret_cast(object); 317 | refcounted->reference(); 318 | tag = 1; 319 | } 320 | ObjectWrapped *p_nodewrapped = (ObjectWrapped *)lua_newuserdatatagged(L, sizeof(ObjectWrapped), tag); 321 | p_nodewrapped->object_id = object->get_instance_id(); 322 | luaL_getmetatable(L, "object"); 323 | lua_setmetatable(L, -2); 324 | } 325 | 326 | void object_userdata_dtor(lua_State *L, void *data) { 327 | ObjectWrapped *p_nodewrapped = reinterpret_cast(data); 328 | godot::Object *object = godot::ObjectDB::get_instance(p_nodewrapped->object_id); 329 | if (object == NULL) return; 330 | godot::RefCounted *refcounted = godot::Object::cast_to(object); 331 | if (refcounted == NULL) return; 332 | refcounted->unreference(); 333 | } 334 | 335 | godot::Object *lua_toobject(lua_State *L, int idx) { 336 | ObjectWrapped *p_nodewrapped = (ObjectWrapped *)lua_touserdata(L, idx); 337 | if (p_nodewrapped == NULL) return NULL; // Not userdata 338 | if (!luaL_hasmetatable(L, idx, "object")) return NULL; // Not "Object" 339 | return godot::ObjectDB::get_instance(p_nodewrapped->object_id); 340 | } 341 | 342 | int lua_isobject(lua_State *L, int idx) { 343 | if (!lua_isuserdata(L, idx)) return 0; // Not userdata 344 | if (!luaL_hasmetatable(L, idx, "object")) return 0; // Not "Object" 345 | return 1; 346 | } 347 | 348 | 349 | bool lua_isvalidobject(lua_State *L, int idx) { 350 | ObjectWrapped *p_nodewrapped = (ObjectWrapped *)luaL_checkudata(L, idx, "object"); 351 | godot::Object *object = godot::ObjectDB::get_instance(p_nodewrapped->object_id); 352 | return object != NULL; 353 | } 354 | 355 | godot::Object *luaL_checkobject(lua_State *L, int idx, bool valid) { 356 | ObjectWrapped *p_nodewrapped = (ObjectWrapped *)luaL_checkudata(L, idx, "object"); 357 | godot::Object *object = godot::ObjectDB::get_instance(p_nodewrapped->object_id); 358 | if (valid && object == NULL) 359 | luaL_typeerrorL(L, idx, "object"); 360 | return NULL; 361 | return object; 362 | } 363 | 364 | 365 | void lua_setnode(lua_State* L, godot::LuauVM* node) { 366 | lua_pushstring(L, GDLUAU_REGISTRY_NODE_KEY); 367 | lua_pushlightuserdata(L, node); 368 | lua_settable(L, LUA_REGISTRYINDEX); 369 | } 370 | 371 | godot::LuauVM* lua_getnode(lua_State* L) { 372 | lua_getfield(L, LUA_REGISTRYINDEX, GDLUAU_REGISTRY_NODE_KEY); 373 | if (!lua_islightuserdata(L, -1)) { 374 | lua_pop(L, 1); 375 | return nullptr; 376 | } 377 | void *userdata = lua_tolightuserdata(L, -1); 378 | lua_pop(L, 1); 379 | return reinterpret_cast(userdata); 380 | } 381 | -------------------------------------------------------------------------------- /src/classes/luau_vm_bindings.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include "luau_vm.h" 7 | 8 | 9 | using namespace godot; 10 | 11 | 12 | void LuauVM::_bind_passthrough_methods() { 13 | 14 | // Constants 15 | { 16 | // Status 17 | BIND_ENUM_CONSTANT(LUA_OK); 18 | BIND_ENUM_CONSTANT(LUA_YIELD); 19 | BIND_ENUM_CONSTANT(LUA_BREAK); 20 | BIND_ENUM_CONSTANT(LUA_ERRERR); 21 | BIND_ENUM_CONSTANT(LUA_ERRMEM); 22 | BIND_ENUM_CONSTANT(LUA_ERRRUN); 23 | BIND_ENUM_CONSTANT(LUA_ERRSYNTAX); 24 | 25 | // Types 26 | BIND_ENUM_CONSTANT(LUA_TNIL); 27 | BIND_ENUM_CONSTANT(LUA_TBOOLEAN); 28 | BIND_ENUM_CONSTANT(LUA_TLIGHTUSERDATA); 29 | BIND_ENUM_CONSTANT(LUA_TNUMBER); 30 | BIND_ENUM_CONSTANT(LUA_TVECTOR); 31 | BIND_ENUM_CONSTANT(LUA_TSTRING); 32 | BIND_ENUM_CONSTANT(LUA_TTABLE); 33 | BIND_ENUM_CONSTANT(LUA_TFUNCTION); 34 | BIND_ENUM_CONSTANT(LUA_TUSERDATA); 35 | BIND_ENUM_CONSTANT(LUA_TTHREAD); 36 | // BIND_ENUM_CONSTANT(LUA_TPROTO); 37 | // BIND_ENUM_CONSTANT(LUA_TUPVAL); 38 | // BIND_ENUM_CONSTANT(LUA_TDEADKEY); 39 | // BIND_ENUM_CONSTANT(LUA_T_COUNT); 40 | 41 | // Libraries 42 | BIND_ENUM_CONSTANT(LUA_BASE_LIB); 43 | BIND_ENUM_CONSTANT(LUA_COROUTINE_LIB); 44 | BIND_ENUM_CONSTANT(LUA_TABLE_LIB); 45 | BIND_ENUM_CONSTANT(LUA_OS_LIB); 46 | BIND_ENUM_CONSTANT(LUA_STRING_LIB); 47 | BIND_ENUM_CONSTANT(LUA_MATH_LIB); 48 | BIND_ENUM_CONSTANT(LUA_VECTOR_LIB); 49 | BIND_ENUM_CONSTANT(LUA_DEBUG_LIB); 50 | BIND_ENUM_CONSTANT(LUA_UTF8_LIB); 51 | BIND_ENUM_CONSTANT(LUA_BIT32_LIB); 52 | 53 | // GC Operations 54 | BIND_ENUM_CONSTANT(LUA_GCSTOP); 55 | BIND_ENUM_CONSTANT(LUA_GCRESTART); 56 | BIND_ENUM_CONSTANT(LUA_GCCOLLECT); 57 | BIND_ENUM_CONSTANT(LUA_GCCOUNT); 58 | BIND_ENUM_CONSTANT(LUA_GCCOUNTB); 59 | BIND_ENUM_CONSTANT(LUA_GCISRUNNING); 60 | BIND_ENUM_CONSTANT(LUA_GCSTEP); 61 | BIND_ENUM_CONSTANT(LUA_GCSETGOAL); 62 | BIND_ENUM_CONSTANT(LUA_GCSETSTEPMUL); 63 | BIND_ENUM_CONSTANT(LUA_GCSETSTEPSIZE); 64 | 65 | // Other 66 | BIND_ENUM_CONSTANT(LUA_REGISTRYINDEX); 67 | BIND_ENUM_CONSTANT(LUA_GLOBALSINDEX); 68 | BIND_ENUM_CONSTANT(LUA_NOREF); 69 | BIND_ENUM_CONSTANT(LUA_REFNIL); 70 | BIND_ENUM_CONSTANT(LUA_MULTRET); 71 | } 72 | 73 | 74 | // Unique to GDLuau 75 | { 76 | ClassDB::bind_method(D_METHOD("lua_pushvariant", "var"), &LuauVM::lua_pushvariant); 77 | ClassDB::bind_method(D_METHOD("lua_pusharray", "arr"), &LuauVM::lua_pusharray); 78 | ClassDB::bind_method(D_METHOD("lua_pushdictionary", "dict"), &LuauVM::lua_pushdictionary); 79 | 80 | ClassDB::bind_method(D_METHOD("lua_tovariant", "index"), &LuauVM::lua_tovariant); 81 | ClassDB::bind_method(D_METHOD("lua_toarray", "index"), &LuauVM::lua_toarray); 82 | ClassDB::bind_method(D_METHOD("lua_todictionary", "index"), &LuauVM::lua_todictionary); 83 | 84 | ClassDB::bind_method(D_METHOD("lua_tofunction", "index"), &LuauVM::lua_tofunction); 85 | ClassDB::bind_method(D_METHOD("lua_pushcallable", "func", "debugname"), &LuauVM::lua_pushcallable, DEFVAL("")); 86 | 87 | ClassDB::bind_method(D_METHOD("lua_pushobject", "object"), &LuauVM::lua_pushobject); 88 | ClassDB::bind_method(D_METHOD("lua_toobject", "index"), &LuauVM::lua_toobject); 89 | ClassDB::bind_method(D_METHOD("lua_isobject", "index"), &LuauVM::lua_isobject); 90 | ClassDB::bind_method(D_METHOD("lua_isvalidobject", "index"), &LuauVM::lua_isvalidobject); 91 | } 92 | 93 | // Default library 94 | { 95 | ClassDB::bind_method(D_METHOD("lua_loadstring", "code", "chunkname"), &LuauVM::load_string, DEFVAL("loadstring")); 96 | ClassDB::bind_method(D_METHOD("lua_dostring", "code", "chunkname"), &LuauVM::do_string, DEFVAL("dostring")); 97 | 98 | ClassDB::bind_method(D_METHOD("lua_setreadonly", "index", "enabled"), &LuauVM::lua_setreadonly); 99 | 100 | ClassDB::bind_method(D_METHOD("lua_call", "nargs", "nresults"), &LuauVM::lua_call); 101 | ClassDB::bind_method(D_METHOD("lua_concat", "n"), &LuauVM::lua_concat); 102 | ClassDB::bind_method(D_METHOD("lua_createtable", "narr", "nrec"), &LuauVM::lua_createtable); 103 | ClassDB::bind_method(D_METHOD("lua_equal", "index1", "index2"), &LuauVM::lua_equal); 104 | ClassDB::bind_method(D_METHOD("lua_error"), &LuauVM::lua_error); 105 | ClassDB::bind_method(D_METHOD("lua_getfenv", "index"), &LuauVM::lua_getfenv); 106 | ClassDB::bind_method(D_METHOD("lua_getfield", "index", "key"), &LuauVM::lua_getfield); 107 | ClassDB::bind_method(D_METHOD("lua_getglobal", "key"), &LuauVM::lua_getglobal); 108 | ClassDB::bind_method(D_METHOD("lua_getmetatable", "index"), &LuauVM::lua_getmetatable); 109 | ClassDB::bind_method(D_METHOD("lua_gettable", "index"), &LuauVM::lua_gettable); 110 | ClassDB::bind_method(D_METHOD("lua_gettop"), &LuauVM::lua_gettop); 111 | ClassDB::bind_method(D_METHOD("lua_insert", "index"), &LuauVM::lua_insert); 112 | ClassDB::bind_method(D_METHOD("lua_isboolean", "index"), &LuauVM::lua_isboolean); 113 | ClassDB::bind_method(D_METHOD("lua_isfunction", "index"), &LuauVM::lua_isfunction); 114 | ClassDB::bind_method(D_METHOD("lua_isnil", "index"), &LuauVM::lua_isnil); 115 | ClassDB::bind_method(D_METHOD("lua_isnone", "index"), &LuauVM::lua_isnone); 116 | ClassDB::bind_method(D_METHOD("lua_isnoneornil", "index"), &LuauVM::lua_isnoneornil); 117 | ClassDB::bind_method(D_METHOD("lua_isnumber", "index"), &LuauVM::lua_isnumber); 118 | ClassDB::bind_method(D_METHOD("lua_isinteger", "index"), &LuauVM::lua_isinteger); 119 | ClassDB::bind_method(D_METHOD("lua_isnumberx", "index"), &LuauVM::lua_isnumberx); 120 | ClassDB::bind_method(D_METHOD("lua_isstring", "index"), &LuauVM::lua_isstring); 121 | ClassDB::bind_method(D_METHOD("lua_isvector", "index"), &LuauVM::lua_isvector); 122 | ClassDB::bind_method(D_METHOD("lua_istable", "index"), &LuauVM::lua_istable); 123 | ClassDB::bind_method(D_METHOD("lua_isthread", "index"), &LuauVM::lua_isthread); 124 | ClassDB::bind_method(D_METHOD("lua_isuserdata", "index"), &LuauVM::lua_isuserdata); 125 | ClassDB::bind_method(D_METHOD("lua_lessthan", "index1", "index2"), &LuauVM::lua_lessthan); 126 | ClassDB::bind_method(D_METHOD("lua_newtable"), &LuauVM::lua_newtable); 127 | ClassDB::bind_method(D_METHOD("lua_next", "index"), &LuauVM::lua_next); 128 | ClassDB::bind_method(D_METHOD("lua_objlen", "index"), &LuauVM::lua_objlen); 129 | ClassDB::bind_method(D_METHOD("lua_pcall", "nargs", "nresults", "errfunc"), &LuauVM::lua_pcall, DEFVAL(0)); 130 | ClassDB::bind_method(D_METHOD("lua_pop", "n"), &LuauVM::lua_pop, DEFVAL(1)); 131 | ClassDB::bind_method(D_METHOD("lua_pushnil"), &LuauVM::lua_pushnil); 132 | ClassDB::bind_method(D_METHOD("lua_pushboolean", "b"), &LuauVM::lua_pushboolean); 133 | ClassDB::bind_method(D_METHOD("lua_pushinteger", "x"), &LuauVM::lua_pushinteger); 134 | ClassDB::bind_method(D_METHOD("lua_pushnumber", "x"), &LuauVM::lua_pushnumber); 135 | ClassDB::bind_method(D_METHOD("lua_pushstring", "s"), &LuauVM::lua_pushstring); 136 | ClassDB::bind_method(D_METHOD("lua_pushvector", "v"), &LuauVM::lua_pushvector); 137 | ClassDB::bind_method(D_METHOD("lua_pushvalue", "index"), &LuauVM::lua_pushvalue); 138 | ClassDB::bind_method(D_METHOD("lua_rawequal", "index1", "index2"), &LuauVM::lua_rawequal); 139 | ClassDB::bind_method(D_METHOD("lua_rawget", "index"), &LuauVM::lua_rawget); 140 | ClassDB::bind_method(D_METHOD("lua_rawgeti", "index", "n"), &LuauVM::lua_rawgeti); 141 | ClassDB::bind_method(D_METHOD("lua_rawgetfield", "index", "key"), &LuauVM::lua_rawgetfield); 142 | ClassDB::bind_method(D_METHOD("lua_rawset", "index"), &LuauVM::lua_rawset); 143 | ClassDB::bind_method(D_METHOD("lua_rawseti", "index", "n"), &LuauVM::lua_rawseti); 144 | ClassDB::bind_method(D_METHOD("lua_rawsetfield", "index", "key"), &LuauVM::lua_rawsetfield); 145 | ClassDB::bind_method(D_METHOD("lua_remove", "index"), &LuauVM::lua_remove); 146 | ClassDB::bind_method(D_METHOD("lua_replace", "index"), &LuauVM::lua_replace); 147 | ClassDB::bind_method(D_METHOD("lua_setfenv", "index"), &LuauVM::lua_setfenv); 148 | ClassDB::bind_method(D_METHOD("lua_setfield", "index", "key"), &LuauVM::lua_setfield); 149 | ClassDB::bind_method(D_METHOD("lua_setglobal", "key"), &LuauVM::lua_setglobal); 150 | ClassDB::bind_method(D_METHOD("lua_setmetatable", "index"), &LuauVM::lua_setmetatable); 151 | ClassDB::bind_method(D_METHOD("lua_settable", "index"), &LuauVM::lua_settable); 152 | ClassDB::bind_method(D_METHOD("lua_settop", "index"), &LuauVM::lua_settop); 153 | ClassDB::bind_method(D_METHOD("lua_status"), &LuauVM::lua_status); 154 | ClassDB::bind_method(D_METHOD("lua_toboolean", "index"), &LuauVM::lua_toboolean); 155 | ClassDB::bind_method(D_METHOD("lua_tointeger", "index"), &LuauVM::lua_tointeger); 156 | ClassDB::bind_method(D_METHOD("lua_tonumber", "index"), &LuauVM::lua_tonumber); 157 | ClassDB::bind_method(D_METHOD("lua_tostring", "index"), &LuauVM::lua_tostring); 158 | ClassDB::bind_method(D_METHOD("lua_tovector", "index"), &LuauVM::lua_tovector); 159 | ClassDB::bind_method(D_METHOD("lua_type", "index"), &LuauVM::lua_type); 160 | ClassDB::bind_method(D_METHOD("lua_typename", "type"), &LuauVM::lua_typename); 161 | ClassDB::bind_method(D_METHOD("lua_getupvalue", "index", "n"), &LuauVM::lua_getupvalue); 162 | ClassDB::bind_method(D_METHOD("lua_setupvalue", "index", "n"), &LuauVM::lua_setupvalue); 163 | ClassDB::bind_method(D_METHOD("lua_gc", "what", "data"), &LuauVM::lua_gc, DEFVAL(0)); 164 | 165 | ClassDB::bind_method(D_METHOD("lua_ref", "index"), &LuauVM::lua_ref); 166 | ClassDB::bind_method(D_METHOD("lua_unref", "ref"), &LuauVM::lua_unref); 167 | ClassDB::bind_method(D_METHOD("lua_getref", "ref"), &LuauVM::lua_getref); 168 | 169 | ClassDB::bind_method(D_METHOD("luaL_ref", "index"), &LuauVM::lua_ref); 170 | ClassDB::bind_method(D_METHOD("luaL_unref", "ref"), &LuauVM::lua_unref); 171 | ClassDB::bind_method(D_METHOD("luaL_getref", "ref"), &LuauVM::lua_getref); 172 | } 173 | 174 | // Auxiliary library 175 | { 176 | ClassDB::bind_method(D_METHOD("luaL_hasmetatable", "index", "tname"), &LuauVM::luaL_hasmetatable); 177 | 178 | ClassDB::bind_method(D_METHOD("luaL_error", "s"), &LuauVM::luaL_error); 179 | ClassDB::bind_method(D_METHOD("luaL_callmeta", "obj", "field"), &LuauVM::luaL_callmeta); 180 | ClassDB::bind_method(D_METHOD("luaL_getmetafield", "obj", "field"), &LuauVM::luaL_getmetafield); 181 | ClassDB::bind_method(D_METHOD("luaL_getmetatable", "tname"), &LuauVM::luaL_getmetatable); 182 | ClassDB::bind_method(D_METHOD("luaL_newmetatable", "tname"), &LuauVM::luaL_newmetatable); 183 | ClassDB::bind_method(D_METHOD("luaL_where", "lvl"), &LuauVM::luaL_where); 184 | ClassDB::bind_method(D_METHOD("luaL_typerror", "narg", "tname"), &LuauVM::luaL_typerror); 185 | ClassDB::bind_method(D_METHOD("luaL_argcheck", "cond", "narg", "msg"), &LuauVM::luaL_argcheck); 186 | 187 | ClassDB::bind_method(D_METHOD("luaL_checkany", "narg"), &LuauVM::luaL_checkany); 188 | ClassDB::bind_method(D_METHOD("luaL_checkint", "narg"), &LuauVM::luaL_checkint); 189 | ClassDB::bind_method(D_METHOD("luaL_checkstring", "narg"), &LuauVM::luaL_checkstring); 190 | ClassDB::bind_method(D_METHOD("luaL_checknumber", "narg"), &LuauVM::luaL_checknumber); 191 | ClassDB::bind_method(D_METHOD("luaL_checkvector", "narg"), &LuauVM::luaL_checkvector); 192 | ClassDB::bind_method(D_METHOD("luaL_checkobject", "narg", "valid"), &LuauVM::luaL_checkobject); 193 | ClassDB::bind_method(D_METHOD("luaL_checktype", "narg", "type"), &LuauVM::luaL_checktype); 194 | ClassDB::bind_method(D_METHOD("luaL_checkstack", "size", "message"), &LuauVM::luaL_checkstack); 195 | // ClassDB::bind_method(D_METHOD("luaL_checkudata", "")) 196 | ClassDB::bind_method(D_METHOD("luaL_checkoption", "narg", "array", "def"), &LuauVM::luaL_checkoption, DEFVAL("")); 197 | } 198 | } 199 | 200 | 201 | #pragma region GDLuau 202 | 203 | void LuauVM::lua_pushvariant(const Variant &var) { 204 | ::lua_pushvariant(L, var); 205 | } 206 | 207 | void LuauVM::lua_pusharray(const Array &array) { 208 | ::lua_pusharray(L, array); 209 | } 210 | 211 | void LuauVM::lua_pushdictionary(const Dictionary &dictionary) { 212 | ::lua_pushdictionary(L, dictionary); 213 | } 214 | 215 | Variant LuauVM::lua_tovariant(int index) { 216 | return ::lua_tovariant(L, index); 217 | } 218 | 219 | Array LuauVM::lua_toarray(int index) { 220 | return ::lua_toarray(L, index); 221 | } 222 | 223 | Dictionary LuauVM::lua_todictionary(int index) { 224 | return ::lua_todictionary(L, index); 225 | } 226 | 227 | Ref LuauVM::lua_tofunction(int index) { 228 | return ::lua_tofunction(L, index); 229 | } 230 | 231 | Error LuauVM::lua_pushcallable(const Callable &callable, const String &debugname) { 232 | ERR_FAIL_COND_V_MSG(callable.is_custom(), ERR_INVALID_PARAMETER, "Cannot push callable, lambda callables aren't supported."); 233 | ERR_FAIL_COND_V_MSG(callable.is_null(), ERR_INVALID_PARAMETER, "Cannot push callable, the provided callable is invalid."); 234 | ::lua_pushcallable(L, callable, debugname); 235 | return OK; 236 | } 237 | 238 | void LuauVM::lua_pushobject(Object *object) { 239 | ::lua_pushobject(L, object); 240 | } 241 | 242 | godot::Object *LuauVM::lua_toobject(int index) { 243 | return ::lua_toobject(L, index); 244 | } 245 | 246 | bool LuauVM::lua_isobject(int index) { 247 | return ::lua_isobject(L, index); 248 | } 249 | 250 | #pragma endregion 251 | 252 | 253 | #pragma region Default 254 | 255 | void LuauVM::lua_setreadonly(int index, bool enabled) { 256 | ::lua_setreadonly(L, index, enabled); 257 | } 258 | 259 | void LuauVM::lua_call(int nargs, int nresults) { 260 | ::lua_call(L, nargs, nresults); 261 | } 262 | 263 | void LuauVM::lua_concat(int n) { 264 | ::lua_concat(L, n); 265 | } 266 | 267 | void LuauVM::lua_createtable(int narr, int nrec) { 268 | ::lua_createtable(L, narr, nrec); 269 | } 270 | 271 | void LuauVM::lua_equal(int index1, int index2) { 272 | ::lua_equal(L, index1, index2); 273 | } 274 | 275 | void LuauVM::lua_error() { 276 | ::lua_error(L); 277 | } 278 | 279 | void LuauVM::lua_getfenv(int index) { 280 | ::lua_getfenv(L, index); 281 | } 282 | 283 | int LuauVM::lua_getfield(int index, const String &key) { 284 | return ::lua_getfield(L, index, key.ascii().get_data()); 285 | } 286 | 287 | void (LuauVM::lua_getglobal)(const String &key) { 288 | ::lua_getfield(L, LUA_GLOBALSINDEX, key.ascii().get_data()); 289 | } 290 | 291 | bool LuauVM::lua_getmetatable(int index) { 292 | return ::lua_getmetatable(L, index) != 0; 293 | } 294 | 295 | int LuauVM::lua_gettable(int index) { 296 | return ::lua_gettable(L, index); 297 | } 298 | 299 | int LuauVM::lua_gettop() { 300 | return ::lua_gettop(L); 301 | } 302 | 303 | void LuauVM::lua_insert(int index) { 304 | ::lua_insert(L, index); 305 | } 306 | 307 | bool (LuauVM::lua_isboolean)(int index) { 308 | return lua_type(index) == LUA_TBOOLEAN; 309 | } 310 | 311 | bool (LuauVM::lua_isfunction)(int index) { 312 | return lua_type(index) == LUA_TFUNCTION; 313 | } 314 | 315 | bool (LuauVM::lua_isnil)(int index) { 316 | return lua_type(index) == LUA_TNIL; 317 | } 318 | 319 | bool (LuauVM::lua_isnone)(int index) { 320 | return lua_type(index) == LUA_TNONE; 321 | } 322 | 323 | bool (LuauVM::lua_isnoneornil)(int index) { 324 | return lua_type(index) <= LUA_TNIL; 325 | } 326 | 327 | bool LuauVM::lua_isnumber(int index) { 328 | return ::lua_isnumber(L, index); 329 | } 330 | 331 | bool LuauVM::lua_isinteger(int index) { 332 | if (!lua_isnumber(index)) 333 | return false; 334 | double x = (lua_tonumber)(index); 335 | return x == (int)x; 336 | } 337 | 338 | bool (LuauVM::lua_isnumberx)(int index) { 339 | int isnum; 340 | ::lua_tonumberx(L, index, &isnum); 341 | return isnum; 342 | } 343 | 344 | 345 | bool LuauVM::lua_isstring(int index) { 346 | return ::lua_isstring(L, index); 347 | } 348 | 349 | bool (LuauVM::lua_isvector)(int index) { 350 | return lua_type(index) == LUA_TVECTOR; 351 | } 352 | 353 | bool (LuauVM::lua_istable)(int index) { 354 | return lua_type(index) == LUA_TTABLE; 355 | } 356 | 357 | bool (LuauVM::lua_isthread)(int index) { 358 | return lua_type(index) == LUA_TTHREAD; 359 | } 360 | 361 | bool LuauVM::lua_isuserdata(int index) { 362 | return ::lua_isuserdata(L, index); 363 | } 364 | 365 | bool LuauVM::lua_lessthan(int index1, int index2) { 366 | return ::lua_lessthan(L, index1, index2); 367 | } 368 | 369 | void (LuauVM::lua_newtable)() { 370 | ::lua_newtable(L); 371 | } 372 | 373 | bool LuauVM::lua_next(int index) { 374 | return ::lua_next(L, index); 375 | } 376 | 377 | int64_t LuauVM::lua_objlen(int index) { 378 | return ::lua_objlen(L, index); 379 | } 380 | 381 | int LuauVM::lua_pcall(int nargs, int nresults, int errfunc) { 382 | return ::lua_pcall(L, nargs, nresults, errfunc); 383 | } 384 | 385 | void (LuauVM::lua_pop)(int n) { 386 | ::lua_pop(L, n); 387 | } 388 | 389 | void LuauVM::lua_pushnil() { 390 | ::lua_pushnil(L); 391 | } 392 | 393 | void LuauVM::lua_pushboolean(bool b) { 394 | ::lua_pushboolean(L, b); 395 | } 396 | 397 | void LuauVM::lua_pushinteger(int x) { 398 | ::lua_pushinteger(L, x); 399 | } 400 | 401 | void LuauVM::lua_pushnumber(float x) { 402 | ::lua_pushnumber(L, x); 403 | } 404 | 405 | void LuauVM::lua_pushstring(const String &s) { 406 | ::lua_pushstring(L, s.ascii().get_data()); 407 | } 408 | 409 | #if LUA_VECTOR_SIZE == 4 410 | void LuauVM::lua_pushvector(Vector4 v) { 411 | ::lua_pushvector(L, v.x, v.y, v.z, v.w); 412 | } 413 | #else 414 | void LuauVM::lua_pushvector(Vector3 v) { 415 | ::lua_pushvector(L, v.x, v.y, v.z); 416 | } 417 | #endif 418 | 419 | void LuauVM::lua_pushvalue(int index) { 420 | ::lua_pushvalue(L, index); 421 | } 422 | 423 | bool LuauVM::lua_rawequal(int index1, int index2) { 424 | return ::lua_rawequal(L, index1, index2) != 0; 425 | } 426 | 427 | void LuauVM::lua_rawget(int index) { 428 | ::lua_rawget(L, index); 429 | } 430 | 431 | void LuauVM::lua_rawgeti(int index, int n) { 432 | ::lua_rawgeti(L, index, n); 433 | } 434 | 435 | void LuauVM::lua_rawgetfield(int index, const String &key) { 436 | ::lua_rawgetfield(L, index, key.ascii().get_data()); 437 | } 438 | 439 | void LuauVM::lua_rawset(int index) { 440 | ::lua_rawset(L, index); 441 | } 442 | 443 | void LuauVM::lua_rawseti(int index, int n) { 444 | ::lua_rawseti(L, index, n); 445 | } 446 | 447 | void LuauVM::lua_rawsetfield(int index, const String &key) { 448 | ::lua_rawsetfield(L, index, key.ascii().get_data()); 449 | } 450 | 451 | void LuauVM::lua_remove(int index) { 452 | ::lua_remove(L, index); 453 | } 454 | 455 | void LuauVM::lua_replace(int index) { 456 | ::lua_replace(L, index); 457 | } 458 | 459 | bool LuauVM::lua_setfenv(int index) { 460 | return ::lua_setfenv(L, index); 461 | } 462 | 463 | void LuauVM::lua_setfield(int index, const String &key) { 464 | ::lua_setfield(L, index, key.ascii().get_data()); 465 | } 466 | 467 | void (LuauVM::lua_setglobal)(const String &key) { 468 | ::lua_setglobal(L, key.ascii().get_data()); 469 | } 470 | 471 | bool LuauVM::lua_setmetatable(int index) { 472 | return ::lua_setmetatable(L, index); 473 | } 474 | 475 | void LuauVM::lua_settable(int index) { 476 | ::lua_settable(L, index); 477 | } 478 | 479 | void LuauVM::lua_settop(int index) { 480 | ::lua_settop(L, index); 481 | } 482 | 483 | int LuauVM::lua_status() { 484 | return ::lua_status(L); 485 | } 486 | 487 | bool LuauVM::lua_toboolean(int index) { 488 | return ::lua_toboolean(L, index); 489 | } 490 | 491 | int64_t (LuauVM::lua_tointeger)(int index) { 492 | return ::lua_tointeger(L, index); 493 | } 494 | 495 | double (LuauVM::lua_tonumber)(int index) { 496 | return ::lua_tonumber(L, index); 497 | } 498 | 499 | String (LuauVM::lua_tostring)(int index) { 500 | return String(::lua_tostring(L, index)); 501 | } 502 | 503 | #if LUA_VECTOR_SIZE == 4 504 | Vector4 LuauVM::lua_tovector(int index) { 505 | const float* vec = ::lua_tovector(L, index); 506 | return Vector4(vec[0], vec[1], vec[2], vec[3]); 507 | } 508 | #else 509 | Vector3 LuauVM::lua_tovector(int index) { 510 | const float* vec = ::lua_tovector(L, index); 511 | return Vector3(vec[0], vec[1], vec[2]); 512 | } 513 | #endif 514 | 515 | int LuauVM::lua_type(int index) { 516 | return ::lua_type(L, index); 517 | } 518 | 519 | String LuauVM::lua_typename(int type) { 520 | return String(::lua_typename(L, type)); 521 | } 522 | 523 | String LuauVM::lua_getupvalue(int index, int n) { 524 | return String(::lua_getupvalue(L, index, n)); 525 | } 526 | 527 | String LuauVM::lua_setupvalue(int index, int n) { 528 | return String(::lua_setupvalue(L, index, n)); 529 | } 530 | 531 | int LuauVM::lua_gc(int what, int data) { 532 | return ::lua_gc(L, what, data); 533 | } 534 | 535 | int LuauVM::lua_ref(int index) { 536 | return ::lua_ref(L, index); 537 | } 538 | 539 | void LuauVM::lua_unref(int ref) { 540 | ::lua_unref(L, ref); 541 | } 542 | 543 | int (LuauVM::lua_getref)(int ref) { 544 | return ::lua_getref(L, ref); 545 | } 546 | 547 | // lua_resume 548 | // lua_touserdata 549 | // lua_gethook 550 | // lua_gethookcount 551 | // lua_gethookmask 552 | // lua_getinfo 553 | // lua_getlocal 554 | // lua_getstack 555 | // lua_sethook 556 | // lua_setlocal 557 | 558 | #pragma endregion 559 | 560 | 561 | #pragma region Auxiliary 562 | 563 | 564 | bool LuauVM::luaL_hasmetatable(int index, const String &tname) { 565 | return ::luaL_hasmetatable(L, index, tname.ascii().get_data()); 566 | } 567 | 568 | void (LuauVM::luaL_error)(const String &err) { 569 | ::luaL_error(L, err.ascii().get_data()); 570 | } 571 | 572 | bool LuauVM::luaL_callmeta(int obj, const String &field) { 573 | return ::luaL_callmeta(L, obj, field.ascii().get_data()); 574 | } 575 | 576 | bool LuauVM::luaL_getmetafield(int obj, const String &field) { 577 | return ::luaL_getmetafield(L, obj, field.ascii().get_data()); 578 | } 579 | 580 | bool (LuauVM::luaL_getmetatable)(const String &tname) { 581 | return ::lua_getfield(L, LUA_REGISTRYINDEX, tname.ascii().get_data()); 582 | } 583 | 584 | bool LuauVM::luaL_newmetatable(const String &tname) { 585 | return ::luaL_newmetatable(L, tname.ascii().get_data()); 586 | } 587 | 588 | void LuauVM::luaL_where(int lvl) { 589 | ::luaL_where(L, lvl); 590 | } 591 | 592 | void LuauVM::luaL_typerror(int nargs, const String &tname) { 593 | ::luaL_typeerror(L, nargs, tname.ascii().get_data()); 594 | } 595 | 596 | void (LuauVM::luaL_argcheck)(bool cond, int narg, const String &msg) { 597 | ((void)((cond) ? (void)0 : ::luaL_argerror(L, narg, msg.ascii().get_data()))); 598 | } 599 | 600 | 601 | void LuauVM::luaL_checkany(int narg) { 602 | ::luaL_checkany(L, narg); 603 | } 604 | 605 | int LuauVM::luaL_checkint(int narg) { 606 | return ::luaL_checkinteger(L, narg); 607 | } 608 | 609 | double LuauVM::luaL_checknumber(int narg) { 610 | return ::luaL_checknumber(L, narg); 611 | } 612 | 613 | String (LuauVM::luaL_checkstring)(int narg) { 614 | return ::luaL_checklstring(L, narg, NULL); 615 | } 616 | 617 | 618 | #if LUA_VECTOR_SIZE == 4 619 | Vector4 LuauVM::luaL_checkvector(int narg) { 620 | const float *v = ::luaL_checkvector(L, narg); 621 | return Vector4(v[0], v[1], v[2], v[3]); 622 | } 623 | #else 624 | Vector3 LuauVM::luaL_checkvector(int narg) { 625 | const float *v = ::luaL_checkvector(L, narg); 626 | return Vector3(v[0], v[1], v[2]); 627 | } 628 | #endif 629 | 630 | bool LuauVM::lua_isvalidobject(int index) { 631 | return ::lua_isvalidobject(L, index); 632 | } 633 | 634 | Object *LuauVM::luaL_checkobject(int narg, bool valid) { 635 | return ::luaL_checkobject(L, narg, valid); 636 | } 637 | 638 | void LuauVM::luaL_checktype(int narg, int type) { 639 | ::luaL_checktype(L, narg, type); 640 | } 641 | 642 | void LuauVM::luaL_checkstack(int sz, const String &messsage) { 643 | ::luaL_checkstack(L, sz, messsage.ascii().get_data()); 644 | } 645 | 646 | // luaL_checkudata 647 | 648 | int LuauVM::luaL_checkoption(int narg, const Array &array, const String &def) { 649 | auto size = array.size(); 650 | const char * * lst = (const char * *)memalloc(sizeof(const char *) * size); 651 | for (int64_t i = 0; i < size; i++) { 652 | Variant v = array[i]; 653 | switch (v.get_type()) { 654 | case Variant::Type::STRING: 655 | case Variant::Type::STRING_NAME: 656 | lst[i] = (v.operator godot::String()).ascii().get_data(); 657 | default: 658 | lst[i] = ""; 659 | } 660 | } 661 | 662 | const char *def_p = NULL; 663 | if (!def.is_empty()) 664 | def_p = def.ascii().get_data(); 665 | int result = ::luaL_checkoption(L, narg, def_p, lst); 666 | memfree(lst); 667 | 668 | return result; 669 | } 670 | 671 | 672 | // luaL_optint 673 | // luaL_optinteger 674 | // luaL_optlong 675 | // luaL_optlstring 676 | // luaL_optnumber 677 | // luaL_optstring 678 | 679 | 680 | #pragma endregion 681 | --------------------------------------------------------------------------------