├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── build └── .gdignore ├── config.ld ├── extras ├── .gdignore ├── articles │ ├── 1-design-en.md │ ├── 1-design-pt.md │ ├── 2-create-gdnativelibrary-save.png │ ├── 2-create-gdnativelibrary.png │ ├── 2-create-resource.png │ ├── 2-infrastructure-en.md │ ├── 2-infrastructure-pt.md │ ├── 2-pick-so-save.png │ ├── 2-pick-so.png │ ├── 2-pluginscript-xmake-lua.png │ ├── 2-set-singleton.png │ ├── 2-settings-gdnative-enabled.png │ ├── 3-luajit-callbacks-en.md │ ├── 3-luajit-callbacks-pt.md │ └── 3-script-init-xmake-lua.png ├── docs │ ├── building.md │ ├── configuring.md │ ├── from-gdscript-to-lua.md │ ├── limitations.md │ ├── luarocks.md │ ├── plugin-enabling.png │ ├── plugin-repl.png │ └── plugin.md └── icon.png ├── lib └── .gdignore ├── lps_coroutine.lua ├── lua_pluginscript.gdnlib ├── plugin ├── export_plugin.lua ├── in_editor_callbacks │ ├── .gdignore │ └── init.lua ├── lua_repl.lua ├── lua_repl.tscn ├── luasrcdiet │ └── .gdignore ├── plugin.cfg └── plugin.gd └── src ├── .gdignore ├── cache_lua_libs.lua ├── godot_aabb.lua ├── godot_array.lua ├── godot_array_commons.lua ├── godot_basis.lua ├── godot_class.lua ├── godot_color.lua ├── godot_dictionary.lua ├── godot_enums.lua ├── godot_ffi.lua ├── godot_node_path.lua ├── godot_object.lua ├── godot_plane.lua ├── godot_pool_byte_array.lua ├── godot_pool_color_array.lua ├── godot_pool_int_array.lua ├── godot_pool_real_array.lua ├── godot_pool_string_array.lua ├── godot_pool_vector2_array.lua ├── godot_pool_vector3_array.lua ├── godot_quat.lua ├── godot_rect2.lua ├── godot_rid.lua ├── godot_string.lua ├── godot_string_name.lua ├── godot_transform.lua ├── godot_transform2d.lua ├── godot_variant.lua ├── godot_vector2.lua ├── godot_vector3.lua ├── language_gdnative.c ├── late_globals.lua ├── lua_globals.lua ├── lua_math_extras.lua ├── lua_object_struct.lua ├── lua_object_wrapper.lua ├── lua_package_extras.lua ├── lua_string_extras.lua ├── pluginscript_callbacks.lua ├── pluginscript_instance.lua ├── pluginscript_property.lua ├── pluginscript_script.lua ├── pluginscript_signal.lua ├── register_in_editor_callbacks.lua ├── test ├── array.lua ├── class_wrapper.lua ├── coroutines.lua ├── extras │ ├── invalid_extends.lua │ ├── parse_error.lua │ ├── valid_script.lua │ └── valid_script_class_wrapper.lua ├── init.lua ├── require_luac.lua ├── script_loading.lua ├── setter_newindex.lua └── test_cmodule.c └── tools ├── add_script_c_decl.sed ├── compact_c_ffi.sed ├── embed_to_c.sed ├── project.godot ├── remove_lua_comments.sed └── squeeze_blank_lines.sed /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | encoding = utf-8 5 | indent_style = tab 6 | indent_size = 4 7 | 8 | [*.{md,yml,yaml}] 9 | indent_style = space 10 | indent_size = 2 11 | 12 | [*.sed] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [gilzoide] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: gilzoide # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: gilzoide # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: [ 'https://www.paypal.com/donate/?hosted_button_id=BFFW9Z3DBYHBA', 'https://www.buymeacoffee.com/gilzoide'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | defaults: 4 | run: 5 | shell: bash 6 | 7 | env: 8 | DEBUG: 1 9 | 10 | jobs: 11 | build_linux: 12 | name: Build Linux 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | target: 17 | - linux64 18 | - linux32 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | submodules: true 23 | - name: Install dependencies 24 | run: | 25 | sudo apt-get update 26 | sudo apt-get install libc6-dev-i386 27 | - name: Build artifact 28 | run: make ${{ matrix.target }} 29 | - name: Upload artifact 30 | uses: actions/upload-artifact@v3 31 | with: 32 | name: linux-x86-x86_64 33 | path: | 34 | build/linux_x86/liblua_pluginscript.so 35 | build/linux_x86_64/liblua_pluginscript.so 36 | 37 | build_windows: 38 | name: Build Windows 39 | runs-on: ubuntu-latest 40 | strategy: 41 | matrix: 42 | target: 43 | - mingw-windows32 44 | - mingw-windows64 45 | steps: 46 | - uses: actions/checkout@v3 47 | with: 48 | submodules: true 49 | - name: Install dependencies 50 | run: | 51 | sudo apt-get update 52 | sudo apt-get install libc6-dev-i386 gcc-mingw-w64 53 | - name: Build artifact 54 | run: make ${{ matrix.target }} 55 | env: 56 | CC: gcc 57 | - name: Upload artifact 58 | uses: actions/upload-artifact@v3 59 | with: 60 | name: windows-x86-x86_64 61 | path: | 62 | build/windows_x86/lua51.dll 63 | build/windows_x86/lua_pluginscript.dll 64 | build/windows_x86_64/lua51.dll 65 | build/windows_x86_64/lua_pluginscript.dll 66 | 67 | build_osx_ios: 68 | name: Build OSX/iOS 69 | runs-on: macos-latest 70 | strategy: 71 | matrix: 72 | target: 73 | - osx64 74 | - ios64 75 | steps: 76 | - uses: actions/checkout@v3 77 | with: 78 | submodules: true 79 | - name: Build artifact 80 | run: make ${{ matrix.target }} 81 | env: 82 | LUA_BIN: lua5.1 83 | - name: Upload artifact 84 | uses: actions/upload-artifact@v3 85 | with: 86 | name: osx-ios-arm64-x86_64 87 | path: | 88 | build/osx_arm64_x86_64/lua_pluginscript.dylib 89 | build/ios_arm64/lua_pluginscript.dylib 90 | build/ios_simulator_arm64_x86_64/lua_pluginscript.dylib 91 | 92 | build_android: 93 | name: Build Android 94 | runs-on: ubuntu-latest 95 | strategy: 96 | matrix: 97 | target: 98 | - android-armv7a 99 | - android-aarch64 100 | - android-x86 101 | - android-x86_64 102 | env: 103 | ANDROID_NDK_VERSION: 21.4.7075529 104 | steps: 105 | - uses: actions/checkout@v3 106 | with: 107 | submodules: true 108 | - name: Install dependencies 109 | run: | 110 | sudo apt-get update 111 | sudo apt-get install libc6-dev-i386 112 | - name: Setup NDK 113 | run: | 114 | $ANDROID_HOME/tools/bin/sdkmanager --install "ndk;$ANDROID_NDK_VERSION" 115 | echo "ANDROID_NDK_ROOT=$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION" >> $GITHUB_ENV 116 | - name: Build artifact 117 | run: make ${{ matrix.target }} 118 | - name: Upload artifact 119 | uses: actions/upload-artifact@v3 120 | with: 121 | name: android-armv7a-aarch64-x86-x86_64 122 | path: | 123 | build/android_armv7a/liblua_pluginscript.so 124 | build/android_aarch64/liblua_pluginscript.so 125 | build/android_x86/liblua_pluginscript.so 126 | build/android_x86_64/liblua_pluginscript.so 127 | 128 | build_distribution_zip: 129 | name: Build distribution zip 130 | needs: [build_linux, build_windows, build_osx_ios, build_android] 131 | runs-on: ubuntu-latest 132 | steps: 133 | - uses: actions/checkout@v3 134 | with: 135 | submodules: true 136 | - name: Download artifacts 137 | id: download 138 | uses: actions/download-artifact@v3 139 | with: 140 | path: artifacts 141 | - name: Copy artifacts to build folder 142 | run: cp -r ${{ steps.download.outputs.download-path }}/*/* build 143 | - name: Make distribution 144 | run: make dist 145 | - name: Upload artifact 146 | uses: actions/upload-artifact@v3 147 | with: 148 | name: lua_pluginscript 149 | path: | 150 | build/LICENSE 151 | build/addons/godot-lua-pluginscript/** 152 | 153 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | defaults: 4 | run: 5 | shell: bash 6 | 7 | env: 8 | DEBUG: 1 9 | 10 | jobs: 11 | test_linux: 12 | name: Run tests on Linux 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | submodules: true 18 | - name: Install Godot 19 | run: | 20 | curl --location $GODOT_RELEASE_URL --output godot.zip 21 | unzip godot.zip 22 | mv Godot_* godot 23 | touch _sc_ 24 | env: 25 | GODOT_RELEASE_URL: https://github.com/godotengine/godot/releases/download/3.5.1-stable/Godot_v3.5.1-stable_linux_headless.64.zip 26 | - name: Build and test 27 | run: make test-linux64 28 | env: 29 | GODOT_BIN: ./godot 30 | 31 | # Windows GitHub runner does not support creating an OpenGL context for running Godot =/ 32 | # test_windows: 33 | # name: Run tests on Windows 34 | # runs-on: windows-latest 35 | # steps: 36 | # - uses: actions/checkout@v3 37 | # with: 38 | # submodules: true 39 | # - name: Install Godot 40 | # run: | 41 | # curl --location $GODOT_RELEASE_URL --output godot.zip 42 | # unzip godot.zip 43 | # mv Godot_*.exe godot.exe 44 | # touch _sc_ 45 | # env: 46 | # GODOT_RELEASE_URL: https://github.com/godotengine/godot/releases/download/3.5.1-stable/Godot_v3.5.1-stable_win64.exe.zip 47 | # - name: Build and test 48 | # run: make test-windows64 49 | # env: 50 | # CC: gcc 51 | # GODOT_BIN: ./godot.exe 52 | 53 | test_osx: 54 | name: Run tests on OSX 55 | runs-on: macos-latest 56 | steps: 57 | - uses: actions/checkout@v3 58 | with: 59 | submodules: true 60 | - name: Install Godot 61 | run: | 62 | curl --location $GODOT_RELEASE_URL --output godot.zip 63 | unzip godot.zip 64 | env: 65 | GODOT_RELEASE_URL: https://github.com/godotengine/godot/releases/download/3.5.1-stable/Godot_v3.5.1-stable_osx.universal.zip 66 | - name: Build and test 67 | run: make test-osx64 68 | env: 69 | GODOT_BIN: ./Godot.app/Contents/MacOS/Godot 70 | 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | build/ 3 | .xmake/ 4 | .cache/ 5 | /docs/ 6 | plugin/luasrcdiet/* 7 | !plugin/luasrcdiet/.gdignore 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/godot-headers"] 2 | path = lib/godot-headers 3 | url = https://github.com/godotengine/godot-headers.git 4 | [submodule "lib/high-level-gdnative"] 5 | path = lib/high-level-gdnative 6 | url = https://github.com/gilzoide/high-level-gdnative.git 7 | [submodule "lib/luajit"] 8 | path = lib/luajit 9 | url = https://github.com/LuaJIT/LuaJIT.git 10 | [submodule "lib/luasrcdiet"] 11 | path = lib/luasrcdiet 12 | url = https://github.com/jirutka/luasrcdiet.git 13 | [submodule "lib/luaunit"] 14 | path = lib/luaunit 15 | url = https://github.com/bluebird75/luaunit.git 16 | [submodule "lib/debugger_lua"] 17 | path = lib/debugger_lua 18 | url = https://github.com/slembcke/debugger.lua.git 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | ## [Unreleased](https://github.com/gilzoide/godot-lua-pluginscript/compare/0.5.2...HEAD) 3 | 4 | 5 | ## [0.5.2](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/0.5.2) 6 | ### Fixed 7 | 8 | - Fixed `undefined symbol: lua_*` when requiring Lua/C modules in POSIX systems 9 | ([#41](https://github.com/gilzoide/godot-lua-pluginscript/issues/41)) 10 | 11 | 12 | ## [0.5.1](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/0.5.1) 13 | ### Fixed 14 | 15 | - Plugin initialization on Windows ([#31](https://github.com/gilzoide/godot-lua-pluginscript/issues/31)) 16 | - [build] Fixed `make dist` dependencies to include LuaJIT's `jit/*.lua` files 17 | - [build] Fixed `make unzip-to-build` to only copy contents from `build` folder 18 | 19 | 20 | ## [0.5.0](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/0.5.0) 21 | ### Added 22 | 23 | - `join` method for all `Pool*Array` metatypes with the same implementation as 24 | for `Array` and `PoolStringArray`. 25 | - Possibility for using `ClassWrapper`s as `extends` of a script instead of 26 | their string name. E.g. script: `return { extends = Node }` 27 | - `Object:get_class_wrapper` method that returns the `ClassWrapper` associated 28 | with the Object's class. 29 | - `ClassWrapper:has_property` method that returns whether a class has a property 30 | with the passed name. Properties are considered available if they are found in 31 | the result of `ClassDB:class_get_property_list`. 32 | - Library mapping for `Server` platform pointing to the `linux_x86_64` build. 33 | - `string.quote` function for quoting values. 34 | - [build] Passing `DEBUG=1` to `make docs` adds `--all` flag to `ldoc`, which 35 | adds documentation for locals. 36 | - [build] Passing `DEBUG_INTERACTIVE=1` to `make test*` makes errors trigger 37 | a [debugger.lua](https://github.com/slembcke/debugger.lua) breakpoint, for 38 | debugging tests. 39 | 40 | ### Fixed 41 | 42 | - Return values passed to `lps_coroutine:resume(...)` when calling `GD.yield()`. 43 | - Comparing `Array` and `Pool*Array`s against Lua primitives like `nil` and 44 | numbers now return `false` instead of raising. 45 | - Support for `false` as the default value for a property. 46 | 47 | ### Changed 48 | 49 | - **BREAKING CHANGE**: `Array` and `Pool*Array`'s `__index` and `__newindex` 50 | metamethods now use 1-based indices to match Lua tables. 51 | For 0-based indexing, use `get`/`set` or `safe_get`/`safe_set` instead. 52 | - **BREAKING CHANGE**: property setter functions don't receive property name 53 | anymore ([#5](https://github.com/gilzoide/godot-lua-pluginscript/issues/5#issuecomment-999876834)). 54 | That is, instead of `function(self, property_name, value)`, setters now look 55 | like `function(self, value)`. 56 | - **BREAKING CHANGE**: script instances are now structs instead of tables. 57 | They still have a backing table for storing data, but indexing now calls 58 | getter and setter functions for known properties. Use `rawget` and `rawset` 59 | to bypass getter/setter functions and access the data table directly. 60 | ([#5](https://github.com/gilzoide/godot-lua-pluginscript/issues/5)) 61 | - **BREAKING CHANGE**: `Object.call` now raises instead of failing silently. 62 | Use `Object.pcall` to protect from errors. 63 | 64 | 65 | ## [0.4.0](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/0.4.0) 66 | ### Added 67 | 68 | - Support for running without JIT enabled 69 | - Support for iOS builds 70 | - `export` function, an alias for `property` that always marks the property as 71 | exported 72 | 73 | ### Fixed 74 | 75 | - Quote `CODE_SIGN_IDENTITY` argument passed to `codesign` invocations 76 | - ABI mismatch for math types in Linux x86_64 + Mono ([#4](https://github.com/gilzoide/godot-lua-pluginscript/issues/4#issuecomment-985423759)) 77 | 78 | ### Changed 79 | 80 | - **BREAKING CHANGE**: properties are not exported by default. Either pass 81 | a usage with the `PropertyUsage.EDITOR` bit set or call `export` instead of 82 | `property` 83 | 84 | 85 | ## [0.3.1](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/0.3.1) 86 | ### Added 87 | 88 | - Support for `codesign`ing OSX builds directly from make invocation. 89 | The optional parameters are `CODE_SIGN_IDENTITY` and `OTHER_CODE_SIGN_FLAGS`. 90 | - `LUA_BIN` option for specifying a Lua command other than `lua` when building 91 | - `native-luajit` make target, used by CI 92 | - `unzip-to-build` make target, for unzipping artifacts from CI to build folder 93 | 94 | ### Fixed 95 | 96 | - `strip` invocation on OSX builds 97 | - Update build GitHub Actions workflow with newer build pipeline 98 | 99 | ### Changed 100 | 101 | - Added `build/.gdignore` to distribution, to stop Godot from trying to import 102 | `build/jit/*.lua` files 103 | - Added default values for `MACOSX_DEPLOYMENT_TARGET`, making it an optional 104 | parameter for OSX builds 105 | 106 | 107 | ## [0.3.0](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/0.3.0) 108 | ### Added 109 | 110 | - `EditorExportPlugin` for minifying Lua scripts with `LuaSrcDiet` on 111 | release exports. Minification may be turned off with the 112 | `lua_pluginscript/export/minify_on_release_export` project setting. 113 | 114 | ### Changed 115 | 116 | - Release builds' init Lua script are minified with `LuaSrcDiet` and libraries 117 | are now `strip`ed, resulting in smaller dynamic libraries 118 | - HGDN functions are now compiled with static visibility and unused GDNative 119 | extensions are excluded, also resulting in smaller dynamic libraries 120 | - Makefile targets for cross-compiling for Windows were renamed from 121 | `cross-windows*` to `mingw-windows*` 122 | 123 | ### Fixed 124 | 125 | - `PoolByteArray.extend` when called with a string argument 126 | 127 | 128 | ## [0.2.0](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/0.2.0) 129 | ### Added 130 | 131 | - `Array.join` method, similar to `PoolStringArray.join` 132 | - Project Settings for setting up `package.path` and `package.cpath` 133 | - Bundle LuaJIT's `jit/*.lua` modules in build folder 134 | 135 | ### Fixed 136 | 137 | - Error handler now uses `tostring` to stringify the incoming parameter, 138 | avoiding type errors. It also checks for the result of `string.match`, so it 139 | does not errors if the message is not in the format expected. 140 | - Loading of C modules now uses absolute library paths, so that files found in 141 | patterns like `res://*` are correctly loaded. 142 | - Always try loading active library, so that dynamic loader knows about `lua*` 143 | symbols when loading C modules. 144 | - `Pool*Array`s' `__gc` metamethod 145 | 146 | 147 | ## [0.1.0](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/0.1.0) 148 | ### Added 149 | 150 | - `GD._VERSION` 151 | - `Object.null` 152 | - Call `Object.set` on `Object.__newindex` 153 | - Stack trace to error message when loading script fails 154 | - Initialize known properties when instantiating script 155 | - Unit test infrastructure 156 | - Android ARMv7/ARM64/x86/x86_64 builds 157 | - [plugin] REPL history, with up/down keys choosing previous/next line 158 | 159 | ### Fixed 160 | 161 | - Properties with `nil` as default value not accesible from Lua 162 | - Call `Array.duplicate` using API 1.1 instead of 1.0 163 | - `Array.duplicate` and `Array.slice` return value GC 164 | - Call `Dictionary.duplicate` using API 1.2 instead of 1.0 165 | - `VariantType` used for float properties 166 | - Calling `NodePath()` returns an empty NodePath, rather than one with the path `"nil"` 167 | 168 | 169 | ## [r1](https://github.com/gilzoide/godot-lua-pluginscript/releases/tag/r1) 170 | ### Added 171 | 172 | - Lua PluginScript language 173 | - Embedded LuaJIT 174 | - Metatypes for all Godot basic types 175 | - `yield` function similar to GDScript's 176 | - Script validation and template source code 177 | - Editor plugin with a simple REPL 178 | - Package searcher for Lua and C modules that work with paths relative to 179 | the `res://` folder and/or exported games' executable path 180 | - API documentation 181 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2021 Gil Barbosa Reis. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Godot Lua PluginScript 2 | 3 | Lua PluginScript icon 4 | 5 | [![Godot Asset Library Icon](https://img.shields.io/static/v1?style=for-the-badge&logo=godotengine&label=asset%20library&color=478CBF&message=0.5.2)](https://godotengine.org/asset-library/asset/1078) 6 | [![Pluginscript Lua](https://img.shields.io/discord/1056941025559588874.svg?label=Discord&logo=Discord&colorB=7289da&style=for-the-badge)](https://discord.gg/rZJbXZXQWe) 7 | 8 | > **WARNING**: This does not work with Godot 4, since it relies on GDNative, a Godot 3 only technology. 9 | > 10 | > To use Lua as a scripting language in Godot 4, use [Lua GDExtension](https://github.com/gilzoide/lua-gdextension) instead. 11 | 12 | GDNative + PluginScript library that adds support for [Lua](https://www.lua.org/) 13 | as a scripting language in [Godot](https://godotengine.org/) 3. 14 | 15 | Being a GDNative library, recompiling the engine is not required, so anyone 16 | with a built release copied to their project can use it. 17 | Being a PluginScript language, Lua can seamlessly communicate with scripts 18 | written in GDScript / C# / Visual Script and vice-versa. 19 | Since the Godot object model is dynamic at runtime, any Godot objects' 20 | properties/methods can be accessed from Lua, including singletons like `OS`, 21 | `ClassDB` and custom singleton nodes. 22 | This way, one can use the language that best suits the implementation for each 23 | script and all of them can understand each other. 24 | 25 | This plugin is available in the Asset Library as [Lua PluginScript](https://godotengine.org/asset-library/asset/1078). 26 | 27 | For some usage examples, check out [plugin/lua\_repl.lua](plugin/lua_repl.lua) 28 | and [plugin/export\_plugin.lua](plugin/export_plugin.lua). 29 | 30 | Currently, only LuaJIT is supported, since the implementation is based on its 31 | [FFI](https://luajit.org/ext_ffi.html) library. 32 | 33 | 34 | ## Installing 35 | 36 | Either: 37 | 38 | - In Godot Editor, open the [Asset Library tab](https://docs.godotengine.org/en/stable/tutorials/assetlib/using_assetlib.html#in-the-editor), 39 | search for the [Lua PluginScript](https://godotengine.org/asset-library/asset/1078) 40 | asset, download and install it. 41 | - Put a built release of the library into the project folder and restart Godot. 42 | Make sure the `lua_pluginscript.gdnlib` file is located at the 43 | `res://addons/godot-lua-pluginscript` folder. 44 | - Clone this repository as the project's `res://addons/godot-lua-pluginscript` 45 | folder and build for the desired platforms. 46 | 47 | 48 | ## Documentation 49 | 50 | - [From GDScript to Lua](extras/docs/from-gdscript-to-lua.md) 51 | - [Lua-specific API reference](https://gilzoide.github.io/godot-lua-pluginscript/topics/README.md.html) 52 | - [Configuring](extras/docs/configuring.md) 53 | - [Editor plugin (REPL and minify on release export)](extras/docs/plugin.md) 54 | - [Using LuaRocks](extras/docs/luarocks.md) 55 | - [Known limitations](extras/docs/limitations.md) 56 | - [Building](extras/docs/building.md) 57 | - [Changelog](CHANGELOG.md) 58 | 59 | 60 | ## Goals 61 | 62 | - Provide support for Lua as a scripting language in Godot in a way that does 63 | not require compiling the engine from scratch 64 | - Be able to seamlessly communicate with any other language supported by Godot, 65 | like GDScript, Visual Script and C#, in an idiomatic way. 66 | This includes being able to dynamically access any Godot object's properties 67 | and methods using Lua's index/method notation 68 | - Have automatic global access to Godot's singleton objects and custom 69 | singleton nodes 70 | - Simple script description interface that doesn't need `require`ing anything 71 | - Support for LuaJIT and Lua 5.2+ 72 | - Support paths relative to `res://*` and exported game/app executable path for 73 | `require`ing Lua modules 74 | - Have a simple build process, where anyone with the cloned source code and 75 | installed build system + toolchain can build the project in a single step 76 | 77 | 78 | ## Non-goals 79 | 80 | - Provide calls to core Godot classes' methods via native method bindings 81 | - Support multithreading on the Lua side 82 | 83 | 84 | ## Articles 85 | 86 | 1. [Designing Godot Lua PluginScript](https://github.com/gilzoide/godot-lua-pluginscript/blob/main/extras/articles/1-design-en.md) 87 | 2. [Implementing the library's skeleton](https://github.com/gilzoide/godot-lua-pluginscript/blob/main/extras/articles/2-infrastructure-en.md) 88 | 3. [Integrating LuaJIT and FFI](https://github.com/gilzoide/godot-lua-pluginscript/blob/main/extras/articles/3-luajit-callbacks-en.md) 89 | 4. Initializing and finalizing scripts (TODO) 90 | 91 | 92 | ## Script example 93 | 94 | This is an example of how a Lua script looks like. 95 | 96 | ```lua 97 | -- Class definitions are regular Lua tables, to be returned from the script 98 | local MyClass = {} 99 | 100 | -- Optional: set class as tool, defaults to false 101 | MyClass.is_tool = true 102 | 103 | -- Optional: set base class by name, defaults to 'Reference' 104 | MyClass.extends = Node 105 | 106 | -- Optional: give your class a name 107 | MyClass.class_name = 'MyClass' 108 | 109 | -- Declare signals 110 | MyClass.something_happened = signal() 111 | MyClass.something_happened_with_args = signal("arg1", "arg2") 112 | 113 | -- Values defined in table are registered as properties of the class 114 | -- By default, properties are not exported to the editor 115 | MyClass.some_prop = 42 116 | 117 | -- The `property` function adds metadata to defined properties, 118 | -- like setter and getter functions 119 | MyClass.some_prop_with_details = property { 120 | -- ["default_value"] or ["default"] or [1] = property default value 121 | 5, 122 | -- ["type"] or [2] = variant type, optional, inferred from default value 123 | -- All Godot variant type names are defined globally as written in 124 | -- GDScript, like bool, int, float, String, Array, Vector2, etc... 125 | -- Notice that Lua <= 5.2 does not differentiate integers from float 126 | -- numbers, so we should always specify `int` where appropriate 127 | -- or use `int(5)` in the default value instead 128 | type = int, 129 | -- ["get"] or ["getter"] = getter function or method name, optional 130 | get = function(self) 131 | return self.some_prop_with_details 132 | end, 133 | -- ["set"] or ["setter"] = setter function or method name, optional 134 | set = 'set_some_prop_with_details', 135 | -- ["usage"] = property usage, from `enum godot_property_usage_flags` 136 | -- optional, default to `PropertyUsage.NOEDITOR` 137 | usage = PropertyUsage.NOEDITOR, 138 | -- ["hint"] = property hint, from `enum godot_property_hint` 139 | -- optional, default to `PropertyHint.NONE` 140 | hint = PropertyHint.RANGE, 141 | -- ["hint_string"] = property hint text, only required for some hints 142 | hint_string = '1,10', 143 | -- ["rset_mode"] = property remote set mode, from `enum godot_method_rpc_mode` 144 | -- optional, default to `RPCMode.DISABLED` 145 | rset_mode = RPCMode.MASTER, 146 | } 147 | -- The `export` function is an alias for `property` that always exports 148 | -- properties to the editor 149 | MyClass.exported_prop = export { "This property appears in the editor" } 150 | MyClass.another_exported_prop = export { 151 | [[This one also appears in the editor, 152 | now with a multiline TextArea for edition]], 153 | hint = PropertyHint.MULTILINE_TEXT, 154 | } 155 | 156 | -- Functions defined in table are public methods 157 | function MyClass:_ready() -- `function t:f(...)` is an alias for `function t.f(self, ...)` 158 | -- Singletons are available globally 159 | local os_name = OS:get_name() 160 | print("MyClass instance is ready! Running on a " .. os_name .. " system") 161 | 162 | -- There is no `onready` keyword like in GDScript 163 | -- Just get the needed values on `_ready` method 164 | -- Also, Lua doesn't have the `$child_node` syntax, use `get_node` instead 165 | self.some_grandchild_node = self:get_node("some/grandchild_node") 166 | end 167 | 168 | function MyClass:set_some_prop_with_details(value) 169 | self.some_prop_with_details = value 170 | -- Indexing `self` with keys undefined in script will search base 171 | -- class for methods and properties 172 | self:emit_signal("something_happened_with_args", "some_prop_with_details", value) 173 | end 174 | 175 | function MyClass:get_some_prop_doubled() 176 | return self.some_prop * 2 177 | end 178 | 179 | -- In the end, table with class declaration must be returned from script 180 | return MyClass 181 | ``` 182 | 183 | 184 | ## Status 185 | 186 | - [X] LuaJIT support 187 | - [ ] Lua 5.2+ support 188 | - [X] Useful definitions for all GDNative objects, with methods and metamethods 189 | - [X] A `yield` function similar to GDScript's, to resume after a signal is 190 | emitted (`GD.yield`) 191 | - [X] Working PluginScript language definition 192 | - [X] PluginScript script validation and template source code 193 | - [ ] PluginScript code editor callbacks 194 | - [ ] PluginScript debug callbacks 195 | - [ ] PluginScript profiling callbacks 196 | - [X] Package searcher for Lua and C modules that work with paths relative to 197 | the `res://` folder and/or exported games' executable path 198 | - [X] Lua REPL 199 | - [X] API documentation 200 | - [ ] Unit tests 201 | - [ ] Example projects 202 | - [X] Export plugin to minify Lua scripts 203 | - [X] Drop-in binary release in GitHub 204 | - [X] Submit to Asset Library 205 | 206 | 207 | ## Third-party software 208 | 209 | This project uses the following software: 210 | 211 | - [godot-headers](https://github.com/godotengine/godot-headers): headers for 212 | GDNative, distributed under the MIT license 213 | - [LuaJIT](https://luajit.org/luajit.html): Just-In-Time Compiler (JIT) for the 214 | Lua programming language, distributed under the MIT license 215 | - [High Level GDNative (HGDN)](https://github.com/gilzoide/high-level-gdnative): 216 | higher level GDNative API header, released to the Public Domain 217 | - [LuaSrcDiet](https://github.com/jirutka/luasrcdiet): compresses Lua source 218 | code by removing unnecessary characters, distributed under the MIT license 219 | - [LuaUnit](https://github.com/bluebird75/luaunit): unit-testing framework for 220 | Lua, distributed under the BSD license 221 | - [debugger.lua](https://github.com/slembcke/debugger.lua): dependency free, 222 | single file embeddable debugger for Lua, distributed under the MIT license 223 | 224 | 225 | ## Other projects for using Lua in Godot 226 | 227 | - https://github.com/perbone/luascript 228 | - https://github.com/Trey2k/lua 229 | - https://github.com/zozer/godot-lua-module 230 | -------------------------------------------------------------------------------- /build/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/build/.gdignore -------------------------------------------------------------------------------- /config.ld: -------------------------------------------------------------------------------- 1 | project = "Godot Lua PluginScript" 2 | topics = { "CHANGELOG.md", "README.md", "extras/docs" } 3 | format = "markdown" 4 | file = { "src", "lps_coroutine.lua" } 5 | boilerplate = true 6 | sort = true 7 | dir = "docs" 8 | examples = { "lps_coroutine.lua", "plugin/lua_repl.lua" } 9 | -------------------------------------------------------------------------------- /extras/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/.gdignore -------------------------------------------------------------------------------- /extras/articles/1-design-en.md: -------------------------------------------------------------------------------- 1 | # Designing a Godot PluginScript for Lua 2 | 2021-07-28 | `#Godot #Lua #GDNative #PluginScript #languageBindings` | [*Versão em Português*](1-design-pt.md) 3 | 4 | This is the first article in a series about how I'm approaching the development 5 | of a plugin for using the [Lua](https://www.lua.org/) language in 6 | [Godot game engine](https://godotengine.org/). 7 | 8 | Lua is a simple and small, yet powerful and flexible, scripting language. 9 | Although it [isn't fit for every scenario](https://docs.godotengine.org/en/stable/about/faq.html#what-were-the-motivations-behind-creating-gdscript), 10 | it is certainly a great tool for scripting. 11 | Combining that with the power of [LuaJIT](https://luajit.org/), 12 | one of the fastest dynamic language implementations out there, we can also 13 | [call external C functions via the Foreign Function Interface (FFI)](https://luajit.org/ext_ffi.html)! 14 | 15 | With the dynamic nature of scripting in Godot, all supported languages 16 | can seamlessly communicate with each other and thus we can choose to use the 17 | language that best fits the task in hand for each script. 18 | By the means of [signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/signals.html) 19 | and the methods [call](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-call), 20 | [get](https://docs.godotengine.org/en/stable/classes/class_object.html#id1) 21 | and [set](https://docs.godotengine.org/en/stable/classes/class_object.html#id4), 22 | any object can communicate with another one, regardless of the 23 | source language. 24 | 25 | To make Lua be recognized as one of the supported scripting languages for Godot 26 | objects, we will create a PluginScript, which is one of the uses of 27 | [GDNative](https://docs.godotengine.org/en/stable/getting_started/step_by_step/scripting.html#gdnative-c), 28 | the native plugin C API provided by the engine to extend all sorts of 29 | engine systems, such as the scripting one. 30 | One pro of this approach is that only the plugin have to be compiled, 31 | so anyone with a standard prebuilt version of Godot can use it! =D 32 | 33 | 34 | ## Goals 35 | - Provide support for the Lua language in Godot in a way that does not require 36 | compiling the engine from scratch 37 | - Be able to seamlessly communicate with any other language supported by Godot, 38 | like GDScript, Visual Script and C#, in an idiomatic way 39 | - Simple script description interface that doesn't need `require`ing anything 40 | - Support for Lua 5.2+ and LuaJIT 41 | - Have a simple build process, where anyone with the cloned source code and 42 | installed build system + toolchain can build the project in a single step 43 | 44 | 45 | ## Non-goals 46 | - Provide calls to core Godot classes' methods via native method bindings 47 | - Support multithreading on the Lua side 48 | 49 | 50 | ## Script example 51 | This is an example of how a Lua script will look like. There are comments regarding 52 | some design decisions, which may change during development. 53 | 54 | ```lua 55 | -- Class definitions are regular Lua tables, to be returned from the script 56 | local MyClass = {} 57 | 58 | -- Optional: set class as tool, defaults to false 59 | MyClass.is_tool = true 60 | 61 | -- Optional: set base class by name, defaults to 'Reference' 62 | MyClass.extends = 'Node' 63 | 64 | -- Optional: give your class a name 65 | MyClass.class_name = 'MyClass' 66 | 67 | -- Declare signals 68 | MyClass.something_happened = signal() 69 | MyClass.something_happened_with_args = signal("arg1", "arg2") 70 | 71 | -- Values defined in table are registered as properties of the class 72 | MyClass.some_prop = 42 73 | 74 | -- The `property` function adds metadata to defined properties, 75 | -- like setter and getter functions 76 | MyClass.some_prop_with_details = property { 77 | -- [1] or ["default"] or ["default_value"] = property default value 78 | 5, 79 | -- [2] or ["type"] = variant type, optional, inferred from default value 80 | -- All Godot variant type names are defined globally as written in 81 | -- GDScript, like bool, int, float, String, Array, Vector2, etc... 82 | -- Notice that Lua <= 5.2 does not differentiate integers from float 83 | -- numbers, so we should always specify `int` where appropriate 84 | -- or use `int(5)` in the default value instead 85 | type = int, 86 | -- ["set"] or ["setter"] = setter function, optional 87 | set = function(self, value) 88 | self.some_prop_with_details = value 89 | -- Indexing `self` with keys undefined in script will search base 90 | -- class for methods and properties 91 | self:emit_signal("something_happened_with_args", "some_prop_with_details", value) 92 | end, 93 | -- ["get"] or ["getter"] = getter function, optional 94 | get = function(self) 95 | return self.some_prop_with_details 96 | end, 97 | -- ["usage"] = property usage, from enum godot_property_usage_flags 98 | -- optional, default to GD.PROPERTY_USAGE_DEFAULT 99 | usage = GD.PROPERTY_USAGE_DEFAULT, 100 | -- ["hint"] = property hint, from enum godot_property_hint 101 | -- optional, default to GD.PROPERTY_HINT_NONE 102 | hint = GD.PROPERTY_HINT_RANGE, 103 | -- ["hint_string"] = property hint text, only required for some hints 104 | hint_string = '1,10', 105 | -- ["rset_mode"] = property remote set mode, from enum godot_method_rpc_mode 106 | -- optional, default to GD.RPC_MODE_DISABLED 107 | rset_mode = GD.RPC_MODE_MASTER, 108 | } 109 | 110 | -- Functions defined in table are public methods 111 | function MyClass:_init() -- `function t:f(...)` is an alias for `function t.f(self, ...)` 112 | -- Singletons are available globally 113 | local os_name = OS:get_name() 114 | print("MyClass instance initialized! Running on a " .. os_name .. " system") 115 | end 116 | 117 | function MyClass:some_prop_doubled() 118 | return self.some_prop * 2 119 | end 120 | 121 | -- In the end, table with class declaration must be returned from script 122 | return MyClass 123 | ``` 124 | 125 | 126 | ## Implementation design details 127 | PluginScripts have three important concepts: the Language Description, 128 | Script Manifest and Instances. 129 | 130 | Let's check out what each layer is and how they will behave from a high 131 | level perspective: 132 | 133 | 134 | ### Language description 135 | The language description tells Godot how to initialize and finalize our 136 | language runtime, as well as how to load script manifests from source 137 | files. 138 | 139 | When initializing the runtime, a new [lua_State](https://www.lua.org/manual/5.4/manual.html#lua_State) 140 | will be created and Godot functionality setup in it. 141 | The Lua Virtual Machine (VM) will use engine memory management routines, so 142 | that memory is tracked by the performance monitors in debug builds of the 143 | game/application. 144 | All scripts will share this same state. 145 | 146 | There will be a global table named `GD` with some Godot specific 147 | functions, such as [load](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-method-load), 148 | [print](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-method-print), 149 | [push_error](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-method-push-error), 150 | [push_warning](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-method-push-warning) 151 | and [yield](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-method-yield). 152 | Lua's global `print` function will be set to `GD.print` and 153 | [Lua 5.4 warning function](https://www.lua.org/manual/5.4/manual.html#lua_WarnFunction) 154 | will behave like a `push_warning` call. 155 | 156 | Functions that expect file names, like [loadfile](https://www.lua.org/manual/5.4/manual.html#pdf-loadfile) 157 | and [io.open](https://www.lua.org/manual/5.4/manual.html#pdf-io.open), 158 | will be patched to accept paths in the format [`res://*`](https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html#resource-path) 159 | and [`user://*`](https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html#user-path-persistent-data). 160 | Also, a [package searcher](https://www.lua.org/manual/5.4/manual.html#pdf-package.searchers) 161 | will be added so that Lua can [require](https://www.lua.org/manual/5.4/manual.html#pdf-require) 162 | modules from paths relative to `res://`. 163 | 164 | Language finalization will simply [lua_close](https://www.lua.org/manual/5.4/manual.html#lua_close) the state. 165 | 166 | 167 | ### Script manifest 168 | Script manifests hold metadata about classes, such as defined signals, 169 | properties and methods, whether class is [tool](https://docs.godotengine.org/en/stable/tutorials/misc/running_code_in_the_editor.html) 170 | and its base class name. 171 | 172 | In Lua, this information will be stored in Lua tables indexed by the 173 | scripts' path. 174 | 175 | When initializing a script, its source code will be loaded and executed. 176 | Scripts must return a table, which defines the class metadata. 177 | Functions declared in the table are registered as class methods and 178 | other variables are declared as properties or signals. 179 | 180 | Script finalization will destroy the manifest table. 181 | 182 | 183 | ### Instances 184 | When a script is attached to an object, the engine will call our 185 | PluginScript to initialize the instance data and when the object gets 186 | destroyed or gets the script removed, we get to finalize the data. 187 | 188 | In Lua, instance data will be stored in Lua tables indexed by the 189 | instance owner object's memory address. 190 | 191 | When instances are indexed with a key that is not present, methods and 192 | property default values will be searched in the script manifest and its 193 | base class, in that order. 194 | This table will be passed to methods as their first argument, as if 195 | using Lua's method call notation: `instance:method(...)`. 196 | 197 | Instance finalization will destroy the data table. 198 | 199 | 200 | ## Wrapping up 201 | With this high level design in place, we can now start implementing the 202 | plugin! I have created a Git repository for it hosted at 203 | [https://github.com/gilzoide/godot-lua-pluginscript](https://github.com/gilzoide/godot-lua-pluginscript). 204 | 205 | In the [next post](2-infrastructure-en.md) I'll discuss how to build the 206 | necessary infrastructure for the PluginScript to work, with stubs to the 207 | necessary callbacks and a build system that compiles the project in a 208 | single step. 209 | 210 | See you there ;D 211 | -------------------------------------------------------------------------------- /extras/articles/2-create-gdnativelibrary-save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/2-create-gdnativelibrary-save.png -------------------------------------------------------------------------------- /extras/articles/2-create-gdnativelibrary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/2-create-gdnativelibrary.png -------------------------------------------------------------------------------- /extras/articles/2-create-resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/2-create-resource.png -------------------------------------------------------------------------------- /extras/articles/2-pick-so-save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/2-pick-so-save.png -------------------------------------------------------------------------------- /extras/articles/2-pick-so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/2-pick-so.png -------------------------------------------------------------------------------- /extras/articles/2-pluginscript-xmake-lua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/2-pluginscript-xmake-lua.png -------------------------------------------------------------------------------- /extras/articles/2-set-singleton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/2-set-singleton.png -------------------------------------------------------------------------------- /extras/articles/2-settings-gdnative-enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/2-settings-gdnative-enabled.png -------------------------------------------------------------------------------- /extras/articles/3-script-init-xmake-lua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/articles/3-script-init-xmake-lua.png -------------------------------------------------------------------------------- /extras/docs/building.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | ## Submodules 4 | 5 | This project uses git submodules for its dependencies, so be sure to activate 6 | submodules before building. 7 | 8 | ```sh 9 | # clone this repository and activate submodules in a single command 10 | git clone --recurse-submodules https://github.com/gilzoide/godot-lua-pluginscript.git 11 | 12 | # or clone it normally and then activate submodules 13 | git clone https://github.com/gilzoide/godot-lua-pluginscript.git 14 | cd godot-lua-pluginscript 15 | git submodule init 16 | git submodule update 17 | ``` 18 | 19 | ## Libraries 20 | 21 | Build the libraries using [make](https://www.gnu.org/software/make/) from 22 | project root, specifying the system as target: 23 | 24 | ```sh 25 | # Choose one of the supported platforms, based on your operating system 26 | make windows64 # x86_64 27 | make windows32 # x86 28 | make linux64 # x86_64 29 | make linux32 # x86 30 | make osx64 \ # multiarch x86_64 + amd64 dylib 31 | # Optional: deployment target. If absent, uses 10.7 for x86_64 and 11.0 for arm64 32 | MACOSX_DEPLOYMENT_TARGET=XX.YY \ 33 | # Optional: code sign identity. If absent, `codesign` is not performed 34 | CODE_SIGN_IDENTITY= \ 35 | # Optional: additional flags passed to `codesign` 36 | OTHER_CODE_SIGN_FLAGS= 37 | 38 | # Cross-compiling for Windows using MinGW 39 | make mingw-windows64 # x86_64 40 | make mingw-windows32 # x86 41 | 42 | # Cross-compiling for Android using NDK 43 | make android-armv7a \ # Android ARMv7 44 | # Optional: NDK toolchain "bin" folder. Defaults to $ANDROID_NDK_ROOT/toolchains/llvm/prebuild/*/bin 45 | NDK_TOOLCHAIN_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuild/*/bin 46 | make android-aarch64 \ # Android ARM64 47 | NDK_TOOLCHAIN_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuild/*/bin 48 | make android-x86 \ # Android x86 49 | NDK_TOOLCHAIN_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuild/*/bin 50 | make android-x86_64 \ # Android x86_64 51 | NDK_TOOLCHAIN_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuild/*/bin 52 | 53 | # Cross-compiling for iOS in a OSX environment 54 | make ios64 \ # Dylibs for iOS arm64 and simulator arm64 + x86_64 55 | # Optional: minimum iOS version to target. If absent, uses 8.0 56 | IOS_VERSION_MIN=X.Y 57 | # Optional: code sign identity. If absent, `codesign` is not performed 58 | CODE_SIGN_IDENTITY= \ 59 | # Optional: additional flags passed to `codesign` 60 | OTHER_CODE_SIGN_FLAGS= 61 | ``` 62 | 63 | The GDNativeLibrary file `lua_pluginscript.gdnlib` is already configured to use 64 | the built files stored in the `build` folder, so that one can use this 65 | repository directly inside a Godot project under the folder `addons/godot-lua-pluginscript`. 66 | 67 | 68 | ## Export plugin 69 | 70 | If you plan in using the export plugin, the following is also required: 71 | 72 | ```sh 73 | make plugin 74 | ``` 75 | 76 | 77 | ## Distribution ZIP 78 | 79 | After building the desired libraries, a distribution zip can be built with: 80 | 81 | ```sh 82 | make dist 83 | ``` 84 | 85 | 86 | ## API documentation 87 | 88 | The API is documented using [LDoc](https://stevedonovan.github.io/ldoc/manual/doc.md.html) 89 | and may be generated with the following command: 90 | 91 | ```sh 92 | make docs 93 | ``` 94 | -------------------------------------------------------------------------------- /extras/docs/configuring.md: -------------------------------------------------------------------------------- 1 | # Available configurations 2 | 3 | ## Project Settings 4 | In the `Project -> Project Settings...` window, the following configurations are available: 5 | 6 | - **Lua PluginScript/Package Path/Behavior**: Whether templates will replace 7 | [package.path](https://www.lua.org/manual/5.1/manual.html#pdf-package.path), 8 | be appended to it or prepended to it. 9 | Default behavior: `replace`. 10 | - **Lua PluginScript/Package Path/Templates**: List of templates to be 11 | injected into `package.path`. 12 | Default templates: `res://?.lua` and `res://?/init.lua`. 13 | - **Lua PluginScript/Package C Path/Behavior**: Whether templates will replace 14 | [package.cpath](https://www.lua.org/manual/5.1/manual.html#pdf-package.cpath), 15 | be appended to it or prepended to it. 16 | Default behavior: `replace`. 17 | - **Lua PluginScript/Package C Path/Templates**: List of templates to be 18 | injected into `package.cpath`. 19 | Default templates: `!/?.dll` and `!/loadall.dll` on Windows, 20 | `!/?.so` and `!/loadall.so` elsewhere. 21 | - **Lua PluginScript/Export/Minify On Release Export**: Whether Lua scritps 22 | should be minified on release exports. 23 | Defaults to `true`. 24 | 25 | ## Configuring `package.path` and `package.cpath` 26 | Templates for `package.path` and `package.cpath` accept paths starting with 27 | Godot's Resource path `res://` and User path `user://`. 28 | 29 | Also, the special character `!` represents the executable directory. 30 | When running a standalone build, it will be replaced by the directory of the executable path 31 | ([`OS.get_executable_path().get_base_dir()`](https://docs.godotengine.org/en/stable/classes/class_os.html#class-os-method-get-executable-path)). 32 | When opening the project from the editor, it will be replaced by the project root 33 | ([`ProjectSettings.globalize_path("res://")`](https://docs.godotengine.org/en/stable/classes/class_projectsettings.html#class-projectsettings-method-globalize-path)). 34 | 35 | When the behavior is configured to `replace`, paths coming from the environment 36 | variables `LUA_PATH` and `LUA_CPATH` will also be replaced. 37 | 38 | -------------------------------------------------------------------------------- /extras/docs/limitations.md: -------------------------------------------------------------------------------- 1 | # Known limitations 2 | 3 | - Calling methods on Lua scripts from background threads without a proper 4 | threading library for Lua will most likely break, since the Lua engine is not 5 | thread-safe. 6 | - Lua scripts cannot inherit other scripts, not even other Lua scripts at the 7 | moment. 8 | - PluginScript instances in editor are not reloaded when a script is edited. 9 | That means that adding/removing/updating an exported property won't show in 10 | the editor and `tool` scripts won't be reloaded until the project is reopened. 11 | This is a limitation of Godot's PluginScript implementation (tested in Godot 12 | 3.4). 13 | -------------------------------------------------------------------------------- /extras/docs/luarocks.md: -------------------------------------------------------------------------------- 1 | # Using LuaRocks 2 | 3 | Lua modules available at [LuaRocks](https://luarocks.org/) can be installed locally to the project: 4 | 5 | ```sh 6 | luarocks install --lua-version 5.1 --tree 7 | ``` 8 | 9 | **TIP**: put an empty `.gdignore` file in the local modules folder, so that 10 | Godot doesn't try importing the installed `*.lua` files as Lua scripts. 11 | 12 | Adjust the package paths using the [Lua PluginScript project settings](configuring.md) 13 | and scripts should be able to `require` the installed modules. 14 | 15 | For example, if the local modules folder is called `localrocks`, add 16 | `res://localrocks/share/lua/5.1/?.lua` and `res://localrocks/share/lua/5.1/?/init.lua` 17 | to **Package Path** and `res://localrocks/lib/lua/5.1/?.so` (change extension 18 | to `.dll` on Windows and possibly `.dylib` on OSX) to **Package C Path**. 19 | -------------------------------------------------------------------------------- /extras/docs/plugin-enabling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/docs/plugin-enabling.png -------------------------------------------------------------------------------- /extras/docs/plugin-repl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/docs/plugin-repl.png -------------------------------------------------------------------------------- /extras/docs/plugin.md: -------------------------------------------------------------------------------- 1 | # Editor Plugin 2 | 3 | Lua PluginScript comes with an editor plugin that has an interactive Lua 4 | console and an export plugin that minifies Lua scripts. 5 | 6 | 7 | ## Enabling 8 | 9 | The plugin can be enabled in the project's `Project -> Project Settings...` 10 | window, in the `Plugins` tab: 11 | 12 | ![](plugin-enabling.png) 13 | 14 | 15 | ## REPL 16 | 17 | An interactive Lua console is available on the bottom panel, for testing Lua 18 | expressions. 19 | It comes with a basic input history, so that pressing the up/down arrows move 20 | between already given inputs. 21 | Just like Lua/LuaJIT's interactive interpreter, `local` variables do not 22 | persist between expressions, so they should be declared globally if you want to 23 | use their value in a next expression. 24 | 25 | ![](plugin-repl.png) 26 | 27 | 28 | ## Minify on release export 29 | 30 | If the [**Lua PluginScript/Export/Minify On Release Export**](Configuring.md) 31 | setting is enabled, Lua scripts will get minified when exporting a release 32 | version of the game/application. 33 | Minification uses [LuaSrcDiet](https://github.com/jirutka/luasrcdiet) and its 34 | [maximum settings](https://github.com/jirutka/luasrcdiet/blob/master/doc/features-and-usage.adoc#features). 35 | 36 | Notice that minification messes with the scritps' line numbers and consequently 37 | with stack traces. 38 | That is why minification is not available for debug builds. 39 | -------------------------------------------------------------------------------- /extras/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/extras/icon.png -------------------------------------------------------------------------------- /lib/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/lib/.gdignore -------------------------------------------------------------------------------- /lps_coroutine.lua: -------------------------------------------------------------------------------- 1 | -- @file lps_coroutine.lua LuaCoroutine script 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Godot Reference script that wraps a Lua coroutine with an API similar to `GDScriptFunctionState`. 25 | -- These are created by `GD.yield` and won't work if created manually. 26 | -- @script lps_coroutine.lua 27 | 28 | local co_resume, co_status = coroutine.resume, coroutine.status 29 | 30 | -- @type LuaCoroutine 31 | local LuaCoroutine = { 32 | --- `signal completed(result)`: signal emitted by `resume` when the coroutine body is completed. 33 | completed = signal('result'), 34 | --- Result of `coroutine.status` 35 | status = property { 36 | type = string, 37 | get = 'get_status', 38 | }, 39 | } 40 | 41 | --- Returns the `coroutine.status`. 42 | -- @function get_status 43 | -- @treturn string 44 | -- @see coroutine.status 45 | function LuaCoroutine:get_status() 46 | local co = assert(GD.get_lua_instance(self.__data.__address), "no coroutine attached") 47 | return co_status(co) 48 | end 49 | 50 | --- Returns whether coroutine is valid (`self:get_status() ~= 'dead'`). 51 | -- @function is_valid 52 | -- @treturn bool 53 | function LuaCoroutine:is_valid() 54 | return self:get_status() ~= 'dead' 55 | end 56 | 57 | --- Resume a coroutine, similar to `coroutine.resume`. 58 | -- Emits the `completed` signal if the coroutine body is completed. 59 | -- Differently than `GDScriptFunctionState.resume`, this method accepts 60 | -- multiple arguments. 61 | -- @usage 62 | -- local coro = some_object:method_that_yields() 63 | -- local first_value = coro:resume() 64 | -- local second_value = coro:resume() 65 | -- while coro:is_valid() do 66 | -- local next_value = coro:resume() 67 | -- -- do something 68 | -- end 69 | -- @function resume 70 | -- @param ... 71 | -- @return 72 | -- @raise If `coroutine.resume` returns `false` 73 | function LuaCoroutine:resume(...) 74 | local co = assert(GD.get_lua_instance(self.__data.__address), "no coroutine attached") 75 | local _, result = assert(co_resume(co, ...)) 76 | if co_status(co) == 'dead' then 77 | self:emit_signal('completed', result) 78 | end 79 | return result 80 | end 81 | 82 | return LuaCoroutine 83 | -------------------------------------------------------------------------------- /lua_pluginscript.gdnlib: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | singleton=true 4 | load_once=true 5 | symbol_prefix="lps_" 6 | reloadable=false 7 | 8 | [entry] 9 | 10 | Android.armeabi-v7a="res://addons/godot-lua-pluginscript/build/android_armv7a/liblua_pluginscript.so" 11 | Android.arm64-v8a="res://addons/godot-lua-pluginscript/build/android_aarch64/liblua_pluginscript.so" 12 | Android.x86="res://addons/godot-lua-pluginscript/build/android_x86/liblua_pluginscript.so" 13 | Android.x86_64="res://addons/godot-lua-pluginscript/build/android_x86_64/liblua_pluginscript.so" 14 | OSX.64="res://addons/godot-lua-pluginscript/build/osx_arm64_x86_64/lua_pluginscript.dylib" 15 | Windows.64="res://addons/godot-lua-pluginscript/build/windows_x86_64/lua_pluginscript.dll" 16 | Windows.32="res://addons/godot-lua-pluginscript/build/windows_x86/lua_pluginscript.dll" 17 | X11.64="res://addons/godot-lua-pluginscript/build/linux_x86_64/liblua_pluginscript.so" 18 | X11.32="res://addons/godot-lua-pluginscript/build/linux_x86/liblua_pluginscript.so" 19 | iOS.arm64="res://addons/godot-lua-pluginscript/build/ios_arm64/lua_pluginscript.dylib" 20 | iOS.x86_64="res://addons/godot-lua-pluginscript/build/ios_simulator_arm64_x86_64/lua_pluginscript.dylib" 21 | Server="res://addons/godot-lua-pluginscript/build/linux_x86_64/liblua_pluginscript.so" 22 | 23 | [dependencies] 24 | 25 | Android.armeabi-v7a=[ ] 26 | Android.arm64-v8a=[ ] 27 | Android.x86=[ ] 28 | Android.x86_64=[ ] 29 | OSX.64=[ ] 30 | Windows.64=[ "res://addons/godot-lua-pluginscript/build/windows_x86_64/lua51.dll" ] 31 | Windows.32=[ "res://addons/godot-lua-pluginscript/build/windows_x86/lua51.dll" ] 32 | X11.64=[ ] 33 | X11.32=[ ] 34 | iOS.arm64=[ ] 35 | iOS.x86_64=[ ] 36 | -------------------------------------------------------------------------------- /plugin/export_plugin.lua: -------------------------------------------------------------------------------- 1 | -- @file plugin/export_plugin.lua EditorExportPlugin for minifying Lua scripts on release exports 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | local package_path = package.path 24 | package.path = 'res://addons/godot-lua-pluginscript/plugin/?.lua;res://addons/godot-lua-pluginscript/plugin/?/init.lua;' .. package_path 25 | local luasrcdiet = require 'luasrcdiet' 26 | package.path = package_path 27 | 28 | local LuaExportPlugin = { 29 | is_tool = true, 30 | extends = 'EditorExportPlugin', 31 | } 32 | 33 | local SHOULD_MINIFY_RELEASE_SETTING = 'lua_pluginscript/export/minify_on_release_export' 34 | 35 | local function add_project_setting(name, initial_value) 36 | if not ProjectSettings:has_setting(name) then 37 | ProjectSettings:set_setting(name, initial_value) 38 | end 39 | ProjectSettings:set_initial_value(name, initial_value) 40 | end 41 | add_project_setting(SHOULD_MINIFY_RELEASE_SETTING, true) 42 | 43 | function LuaExportPlugin:_export_begin(features, is_debug, path, flags) 44 | self.ignore_path = self:get_script().resource_path:get_base_dir() 45 | self.should_minify = not is_debug and ProjectSettings:get_setting(SHOULD_MINIFY_RELEASE_SETTING) 46 | self.file = File:new() 47 | end 48 | 49 | function LuaExportPlugin:_export_file(path, type, features) 50 | if path:begins_with(self.ignore_path) then 51 | self:skip() 52 | elseif self.should_minify and path:ends_with('.lua') then 53 | if self.file:open(path, File.READ) == GD.OK then 54 | local source = tostring(self.file:get_as_text()) 55 | self.file:close() 56 | local optsource = luasrcdiet.optimize(luasrcdiet.MAXIMUM_OPTS, source) 57 | print(string.format('[LuaPluginScript] Minified %s: %s -> %s (%d%% reduction)', 58 | path, 59 | String.humanize_size(#source), 60 | String.humanize_size(#optsource), 61 | 100 - math.floor(#optsource / #source * 100)) 62 | ) 63 | self:add_file(path, PoolByteArray.from(optsource), false) 64 | end 65 | end 66 | end 67 | 68 | function LuaExportPlugin:_export_end() 69 | self.ignore_path = nil 70 | self.file = nil 71 | end 72 | 73 | return LuaExportPlugin 74 | -------------------------------------------------------------------------------- /plugin/in_editor_callbacks/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/plugin/in_editor_callbacks/.gdignore -------------------------------------------------------------------------------- /plugin/in_editor_callbacks/init.lua: -------------------------------------------------------------------------------- 1 | -- @file in_editor_callbacks.lua PluginScript Editor + Debug callbacks implementation 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | local ffi = require 'ffi' 24 | 25 | local pluginscript_callbacks = debug.getregistry().lps_callbacks 26 | local wrap_callback = pluginscript_callbacks.wrap_callback 27 | 28 | -- void (*)(const godot_string *class_name, const godot_string *base_class_name, godot_string *ret) 29 | pluginscript_callbacks.get_template_source_code = wrap_callback(function(class_name, base_class_name, ret) 30 | class_name = ffi.cast('godot_string *', class_name):gsub("[^_%w]", "_") 31 | base_class_name = ffi.cast('godot_string *', base_class_name) 32 | ret = ffi.cast('godot_string *', ret) 33 | 34 | ret[0] = ffi.gc(String('local ' .. class_name .. ' = {\n\textends = "' .. base_class_name .. '",\n}\n\nreturn ' .. class_name), nil) 35 | end) 36 | 37 | -- godot_bool (*)(const godot_string *script, int *line_error, int *col_error, godot_string *test_error, const godot_string *path, godot_pool_string_array *functions) 38 | pluginscript_callbacks.validate = wrap_callback(function(script, line_error, col_error, test_error, path, functions) 39 | script = ffi.cast('godot_string *', script) 40 | line_error = ffi.cast('int *', line_error) 41 | col_error = ffi.cast('int *', col_error) 42 | test_error = ffi.cast('godot_string *', test_error) 43 | path = ffi.cast('godot_string *', path) 44 | functions = ffi.cast('godot_pool_string_array *', functions) 45 | 46 | local f, err = loadstring(tostring(script), tostring(path)) 47 | if not f then 48 | local line, msg = string.match(err, ':(%d+):%s*(.*)') 49 | line_error[0] = tonumber(line) or -1 50 | test_error[0] = ffi.gc(String(msg or err), nil) 51 | end 52 | return f ~= nil 53 | end, true) 54 | 55 | -- void (*)(const godot_string *class_name, const godot_string *name, const godot_pool_string_array *args, godot_string *ret) 56 | pluginscript_callbacks.make_function = wrap_callback(function(class_name, name, args, ret) 57 | class_name = ffi.cast('godot_string *', class_name) 58 | name = ffi.cast('godot_string *', name) 59 | args = ffi.cast('godot_pool_string_array *', args) 60 | ret = ffi.cast('godot_string *', ret) 61 | 62 | local code = string.format('function %s:%s(%s)\n\t\nend', tostring(class_name), tostring(name), tostring(args:join(', '))) 63 | ret[0] = ffi.gc(String(code), nil) 64 | end) 65 | -------------------------------------------------------------------------------- /plugin/lua_repl.lua: -------------------------------------------------------------------------------- 1 | -- @file plugin/lua_repl.lua A tool Node for building a Lua REPL in-editor 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | local LuaREPL = { 25 | is_tool = true, 26 | extends = "Node", 27 | } 28 | 29 | local index_G = { __index = _G } 30 | 31 | local function get_error(text) 32 | text = tostring(text) 33 | return 'Error: ' .. (text:match(":%d+:%s*(.+)") or text) 34 | end 35 | 36 | -- Cache and setup nodes 37 | function LuaREPL:_ready() 38 | self.output = self:get_node("Output") 39 | self.line_edit = self:get_node("Footer/LineEdit") 40 | self.history_button_popup = self:get_node("Header/HistoryButton"):get_popup() 41 | self.history_button_popup:connect("about_to_show", self, "_on_HistoryButton_popup_about_to_show") 42 | self.history_button_popup:connect("id_pressed", self, "_on_HistoryButton_popup_id_pressed") 43 | 44 | self:reset() 45 | end 46 | 47 | -- Resets the Lua environment and REPL history 48 | function LuaREPL:reset() 49 | -- Local environment, to avoid messing up _G 50 | self.env = setmetatable({ 51 | print = function(...) 52 | return self:printf('%s\n', string.join('\t', ...)) 53 | end, 54 | }, index_G) 55 | self.history = PoolStringArray() 56 | self.current_history = 0 57 | 58 | self:clear() 59 | end 60 | 61 | -- Print content to output 62 | function LuaREPL:print(msg) 63 | self.output:add_text(msg) 64 | end 65 | 66 | function LuaREPL:printn(msg) 67 | self:print(msg) 68 | self:print('\n') 69 | end 70 | 71 | function LuaREPL:printf(fmt, ...) 72 | self:print(string.format(tostring(fmt), ...)) 73 | end 74 | 75 | -- Runs a line, printing the results/error 76 | function LuaREPL:dostring(text) 77 | text = text:strip_edges() 78 | if text:empty() then 79 | return 80 | end 81 | 82 | self.history:append(text) 83 | self.current_history = #self.history 84 | self.line_edit:clear() 85 | self:printn(text) 86 | 87 | text = text:gsub('^=', '', 1) -- support for "= value" idiom from Lua 5.1 REPL 88 | local f, err_msg = load('return ' .. text, nil, nil, self.env) 89 | if not f then 90 | f, err_msg = load(text, nil, nil, self.env) 91 | end 92 | if f then 93 | local result = table.pack(pcall(f)) 94 | if not result[1] then 95 | self:printn(get_error(result[2])) 96 | elseif result.n > 1 then 97 | local joined_results = string.join('\t', table.unpack(result, 2, result.n)) 98 | self:printf('Out[%d]: %s\n', #self.history, joined_results) 99 | end 100 | else 101 | self:printn(get_error(err_msg)) 102 | end 103 | self:prompt() 104 | end 105 | 106 | function LuaREPL:prompt() 107 | self:printf('\nIn [%d]: ', #self.history + 1) 108 | end 109 | 110 | -- Clear output text 111 | function LuaREPL:clear() 112 | self.output:clear() 113 | self:prompt() 114 | end 115 | 116 | -- History handlers 117 | function LuaREPL:set_history(index) 118 | if index >= 0 and index <= #self.history then 119 | self.current_history = index 120 | local text = self.history:safe_get(self.current_history) or "" 121 | self.line_edit.text = text 122 | self.line_edit.caret_position = #text 123 | end 124 | end 125 | 126 | function LuaREPL:history_up() 127 | self:set_history(self.current_history - 1) 128 | end 129 | 130 | function LuaREPL:history_down() 131 | self:set_history(self.current_history + 1) 132 | end 133 | 134 | -- Signal handlers 135 | function LuaREPL:_on_LineEdit_text_entered(text) 136 | self:dostring(text) 137 | end 138 | 139 | function LuaREPL:_on_RunButton_pressed() 140 | self:dostring(self.line_edit.text) 141 | end 142 | 143 | function LuaREPL:_on_ClearButton_pressed() 144 | self:clear() 145 | end 146 | 147 | function LuaREPL:_on_ResetButton_pressed() 148 | self:reset() 149 | end 150 | 151 | function LuaREPL:_on_LineEdit_gui_input(event) 152 | if event:is_class("InputEventKey") and event:is_pressed() then 153 | if event.scancode == GD.KEY_UP then 154 | self:history_up() 155 | elseif event.scancode == GD.KEY_DOWN then 156 | self:history_down() 157 | end 158 | end 159 | end 160 | 161 | function LuaREPL:_on_HistoryButton_popup_about_to_show() 162 | self.history_button_popup:clear() 163 | for i, s in ipairs(self.history) do 164 | self.history_button_popup:add_item(s) 165 | end 166 | end 167 | 168 | function LuaREPL:_on_HistoryButton_popup_id_pressed(i) 169 | self:set_history(i) 170 | end 171 | 172 | return LuaREPL 173 | -------------------------------------------------------------------------------- /plugin/lua_repl.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/godot-lua-pluginscript/plugin/lua_repl.lua" type="Script" id=1] 4 | 5 | [node name="LuaREPL" type="VBoxContainer"] 6 | anchor_right = 1.0 7 | anchor_bottom = 1.0 8 | script = ExtResource( 1 ) 9 | __meta__ = { 10 | "_edit_use_anchors_": false 11 | } 12 | 13 | [node name="Header" type="HBoxContainer" parent="."] 14 | margin_right = 1024.0 15 | margin_bottom = 20.0 16 | 17 | [node name="Title" type="Label" parent="Header"] 18 | margin_top = 3.0 19 | margin_right = 862.0 20 | margin_bottom = 17.0 21 | size_flags_horizontal = 3 22 | text = "Lua PluginScript REPL:" 23 | 24 | [node name="HistoryButton" type="MenuButton" parent="Header"] 25 | margin_left = 866.0 26 | margin_right = 924.0 27 | margin_bottom = 20.0 28 | text = "History" 29 | flat = false 30 | 31 | [node name="ResetButton" type="Button" parent="Header"] 32 | margin_left = 928.0 33 | margin_right = 976.0 34 | margin_bottom = 20.0 35 | hint_tooltip = "Reset the Lua environment and REPL history " 36 | text = "Reset" 37 | 38 | [node name="ClearButton" type="Button" parent="Header"] 39 | margin_left = 980.0 40 | margin_right = 1024.0 41 | margin_bottom = 20.0 42 | hint_tooltip = "Clear the output text" 43 | text = "Clear" 44 | 45 | [node name="Output" type="RichTextLabel" parent="."] 46 | margin_top = 24.0 47 | margin_right = 1024.0 48 | margin_bottom = 572.0 49 | rect_min_size = Vector2( 0, 100 ) 50 | focus_mode = 2 51 | size_flags_vertical = 3 52 | bbcode_enabled = true 53 | scroll_following = true 54 | selection_enabled = true 55 | 56 | [node name="Footer" type="HBoxContainer" parent="."] 57 | margin_top = 576.0 58 | margin_right = 1024.0 59 | margin_bottom = 600.0 60 | 61 | [node name="LineEdit" type="LineEdit" parent="Footer"] 62 | margin_right = 984.0 63 | margin_bottom = 24.0 64 | focus_neighbour_top = NodePath(".") 65 | focus_neighbour_bottom = NodePath(".") 66 | size_flags_horizontal = 3 67 | clear_button_enabled = true 68 | 69 | [node name="RunButton" type="Button" parent="Footer"] 70 | margin_left = 988.0 71 | margin_right = 1024.0 72 | margin_bottom = 24.0 73 | text = "Run" 74 | 75 | [connection signal="pressed" from="Header/ResetButton" to="." method="_on_ResetButton_pressed"] 76 | [connection signal="pressed" from="Header/ClearButton" to="." method="_on_ClearButton_pressed"] 77 | [connection signal="gui_input" from="Footer/LineEdit" to="." method="_on_LineEdit_gui_input"] 78 | [connection signal="text_entered" from="Footer/LineEdit" to="." method="_on_LineEdit_text_entered"] 79 | [connection signal="pressed" from="Footer/RunButton" to="." method="_on_RunButton_pressed"] 80 | -------------------------------------------------------------------------------- /plugin/luasrcdiet/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/plugin/luasrcdiet/.gdignore -------------------------------------------------------------------------------- /plugin/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Lua PluginScript" 4 | description="Tools for Lua PluginScript: REPL tab and export plugin for minifying scripts" 5 | author="gilzoide" 6 | version="0.5.2" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /plugin/plugin.gd: -------------------------------------------------------------------------------- 1 | # @file plugin/plugin.gd EditorPlugin registering REPL 2 | # This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | # 4 | # Copyright (C) 2021 Gil Barbosa Reis. 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the “Software”), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | tool 24 | extends EditorPlugin 25 | 26 | var _lua_repl 27 | var _lua_export_plugin 28 | 29 | func _enter_tree() -> void: 30 | _lua_repl = preload("lua_repl.tscn").instance() 31 | add_control_to_bottom_panel(_lua_repl, "Lua REPL") 32 | 33 | _lua_export_plugin = preload("export_plugin.lua").new() 34 | add_export_plugin(_lua_export_plugin) 35 | 36 | 37 | func _exit_tree() -> void: 38 | if _lua_repl: 39 | remove_control_from_bottom_panel(_lua_repl) 40 | _lua_repl.free() 41 | _lua_repl = null 42 | if _lua_export_plugin: 43 | remove_export_plugin(_lua_export_plugin) 44 | _lua_export_plugin.unreference() 45 | _lua_export_plugin = null 46 | -------------------------------------------------------------------------------- /src/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilzoide/godot-lua-pluginscript/9bdbf1f4eeb9d256ee7163c7157c341792cc1023/src/.gdignore -------------------------------------------------------------------------------- /src/cache_lua_libs.lua: -------------------------------------------------------------------------------- 1 | -- @file cache_lua_libs.lua Some Lua globals cached as locals 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | -- Lua globals 25 | local assert, getmetatable, ipairs, pairs, rawget, select, setmetatable, tonumber, tostring, type 26 | = assert, getmetatable, ipairs, pairs, rawget, select, setmetatable, tonumber, tostring, type 27 | 28 | -- Lua globals with fallback for 5.1 29 | local loadstring = loadstring or load 30 | table.unpack = table.unpack or unpack 31 | table.pack = table.pack or function(...) 32 | return { ..., n = select('#', ...) } 33 | end 34 | 35 | -- Bitwise operations for LuaJIT or 5.2 or 5.3+ operators 36 | local bor = bit.bor or bit32.bor or load[[function(a,b)return a|b end]] 37 | 38 | -- Lua library functions 39 | local coroutine_resume, coroutine_running, coroutine_status, coroutine_yield 40 | = coroutine.resume, coroutine.running, coroutine.status, coroutine.yield 41 | local debug_getinfo, debug_traceback 42 | = debug.getinfo, debug.traceback 43 | local package_loadlib 44 | = package.loadlib 45 | local string_byte, string_find, string_format, string_gmatch, string_gsub, string_lower, string_match, string_replace, string_rep, string_reverse, string_sub, string_upper 46 | = string.byte, string.find, string.format, string.gmatch, string.gsub, string.lower, string.match, string.replace, string.rep, string.reverse, string.sub, string.upper 47 | local table_concat, table_insert, table_remove, table_unpack 48 | = table.concat, table.insert, table.remove, table.unpack 49 | 50 | -- custom globals from `src/language_gdnative.c` 51 | local setthreadfunc, touserdata 52 | = setthreadfunc, touserdata 53 | 54 | -- FFI 55 | local ffi_cast, ffi_cdef, ffi_copy, ffi_gc, ffi_istype, ffi_metatype, ffi_new, ffi_sizeof, ffi_string, ffi_typeof 56 | = ffi.cast, ffi.cdef, ffi.copy, ffi.gc, ffi.istype, ffi.metatype, ffi.new, ffi.sizeof, ffi.string, ffi.typeof 57 | local clib = ffi.C 58 | 59 | -- Weak tables 60 | local weak_kv = { __mode = 'kv' } 61 | 62 | -- Forward declarations 63 | local _Object 64 | local LuaScriptInstance 65 | 66 | -- Some useful patterns 67 | local ERROR_LINE_MESSAGE_PATT = ':(%d+):%s*(.*)' 68 | local ERROR_PATH_LINE_MESSAGE_PATT = '"([^"]+)"[^:]*:(%d*):%s*(.*)' 69 | 70 | -- Some useful functions 71 | local function is_not_nil(value) 72 | return type(value) ~= 'nil' 73 | end 74 | 75 | local function first_index_not_nil(obj, ...) 76 | for i = 1, select('#', ...) do 77 | local index = select(i, ...) 78 | local value = obj[index] 79 | if is_not_nil(value) then 80 | return value 81 | end 82 | end 83 | return nil 84 | end 85 | 86 | local function has_length(value) 87 | local t = type(value) 88 | return t == 'table' or t == 'userdata' or t == 'cdata' 89 | end 90 | -------------------------------------------------------------------------------- /src/godot_aabb.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_aabb.lua Wrapper for GDNative's AABB 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- AABB metatype, wrapper for `godot_aabb`. 25 | -- Constructed using the idiom `AABB(...)`, which calls `__new`. 26 | -- typedef union godot_aabb { 27 | -- uint8_t data[24]; 28 | -- float elements[6]; 29 | -- struct { Vector3 position, size; }; 30 | -- } godot_aabb; 31 | -- @classmod AABB 32 | 33 | local methods = { 34 | fillvariant = api.godot_variant_new_aabb, 35 | varianttype = VariantType.AABB, 36 | 37 | --- Returns the volume of the AABB. 38 | -- @function get_area 39 | -- @treturn number 40 | get_area = api.godot_aabb_get_area, 41 | --- Returns `true` if the AABB is flat or empty. 42 | -- @function has_no_area 43 | -- @treturn bool 44 | has_no_area = api.godot_aabb_has_no_area, 45 | --- Returns `true` if the AABB is empty. 46 | -- @function has_no_surface 47 | -- @treturn bool 48 | has_no_surface = api.godot_aabb_has_no_surface, 49 | --- Returns `true` if the AABB overlaps with another. 50 | -- @function intersects 51 | -- @tparam AABB with 52 | -- @treturn bool 53 | intersects = api.godot_aabb_intersects, 54 | --- Returns `true` if this AABB completely encloses another one. 55 | -- @function encloses 56 | -- @tparam AABB with 57 | -- @treturn bool 58 | encloses = api.godot_aabb_encloses, 59 | --- Returns a larger AABB that contains both this AABB and `with`. 60 | -- @function merge 61 | -- @tparam AABB with 62 | -- @treturn AABB 63 | merge = api.godot_aabb_merge, 64 | --- Returns the intersection between two AABB. An empty AABB (size 0,0,0) is returned on failure. 65 | -- @function intersection 66 | -- @tparam AABB with 67 | -- @treturn AABB 68 | intersection = api.godot_aabb_intersection, 69 | --- Returns `true` if the AABB is on both sides of a plane. 70 | -- @function intersects_plane 71 | -- @tparam Plane plane 72 | -- @treturn bool 73 | intersects_plane = api.godot_aabb_intersects_plane, 74 | --- Returns `true` if the AABB intersects the line segment between `from` and `to`. 75 | -- @function intersects_segment 76 | -- @tparam Vector3 from 77 | -- @tparam Vector3 to 78 | -- @treturn bool 79 | intersects_segment = api.godot_aabb_intersects_segment, 80 | --- Returns `true` if the AABB contains a point. 81 | -- @function has_point 82 | -- @tparam Vector3 point 83 | -- @treturn bool 84 | has_point = api.godot_aabb_has_point, 85 | --- Returns the support point in a given `direction`. 86 | -- This is useful for collision detection algorithms. 87 | -- @function get_support 88 | -- @tparam Vector3 direction 89 | -- @treturn Vector3 90 | get_support = api.godot_aabb_get_support, 91 | --- Returns the normalized longest axis of the AABB. 92 | -- @function get_longest_axis 93 | -- @treturn Vector3 94 | get_longest_axis = api.godot_aabb_get_longest_axis, 95 | --- Returns the index of the longest axis of the AABB (according to Vector3's `AXIS_*` constants). 96 | -- @function get_longest_axis_index 97 | -- @treturn int 98 | get_longest_axis_index = api.godot_aabb_get_longest_axis_index, 99 | --- Returns the scalar length of the longest axis of the AABB. 100 | -- @function get_longest_axis_size 101 | -- @treturn number 102 | get_longest_axis_size = api.godot_aabb_get_longest_axis_size, 103 | --- Returns the normalized shortest axis of the AABB. 104 | -- @function get_shortest_axis 105 | -- @treturn Vector3 106 | get_shortest_axis = api.godot_aabb_get_shortest_axis, 107 | --- Returns the index of the shortest axis of the AABB (according to Vector3's `AXIS_*` constants). 108 | -- @function get_shortest_axis_index 109 | -- @treturn int 110 | get_shortest_axis_index = api.godot_aabb_get_shortest_axis_index, 111 | --- Returns the scalar length of the shortest axis of the AABB. 112 | -- @function get_shortest_axis_size 113 | -- @treturn number 114 | get_shortest_axis_size = api.godot_aabb_get_shortest_axis_size, 115 | --- Returns this AABB expanded to include a given `point`. 116 | -- @function expand 117 | -- @tparam Vector3 point 118 | -- @treturn AABB 119 | expand = api.godot_aabb_expand, 120 | --- Returns a copy of the AABB grown a given amount of units towards all the sides. 121 | -- @function grow 122 | -- @tparam number by 123 | -- @treturn AABB 124 | grow = api.godot_aabb_grow, 125 | --- Gets the position of the 8 endpoints of the AABB in space. 126 | -- @function get_endpoint 127 | -- @tparam int index 128 | -- @treturn Vector3 129 | get_endpoint = api.godot_aabb_get_endpoint, 130 | } 131 | 132 | AABB = ffi_metatype('godot_aabb', { 133 | --- AABB constructor, called by the idiom `AABB(...)`. 134 | -- 135 | -- * `AABB()`: all zeros (`AABB() == AABB(Vector3.ZERO, Vector3.ZERO)`) 136 | -- * `AABB(Vector3 position, Vector3 size)`: set position and size 137 | -- * `AABB(AABB other)`: copy values from `other` 138 | -- @function __new 139 | -- @param ... 140 | -- @treturn AABB 141 | __new = function(mt, position, size) 142 | if ffi_istype(mt, position) then 143 | return ffi_new(mt, position) 144 | else 145 | return ffi_new(mt, { position = position, size = size }) 146 | end 147 | end, 148 | __index = methods, 149 | --- Returns a Lua string representation of this AABB. 150 | -- @function __tostring 151 | -- @treturn string 152 | __tostring = gd_tostring, 153 | --- Concatenates values. 154 | -- @function __concat 155 | -- @param a First value, stringified with `GD.str` 156 | -- @param b First value, stringified with `GD.str` 157 | -- @treturn String 158 | __concat = concat_gdvalues, 159 | --- Equality operation 160 | -- If either `a` or `b` are not of type `AABB`, always return `false`. 161 | -- @function __eq 162 | -- @tparam AABB a 163 | -- @tparam AABB b 164 | -- @treturn bool 165 | __eq = function(a, b) 166 | return ffi_istype(AABB, a) and ffi_istype(AABB, b) and a.position == b.position and a.size == b.size 167 | end, 168 | }) 169 | 170 | 171 | -------------------------------------------------------------------------------- /src/godot_array_commons.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_array_commons.lua Common functionality between Array and Pool*Arrays 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | local function array_empty(self) 25 | return #self == 0 26 | end 27 | 28 | local function array_safe_get(self, index) 29 | if index >= 0 and index < #self then 30 | return self:get(index) 31 | end 32 | end 33 | 34 | local function array_safe_set(self, index, value) 35 | assert(index >= 0, "Array index must be non-negative") 36 | if index >= #self then 37 | self:resize(index + 1) 38 | end 39 | self:set(index, value) 40 | end 41 | 42 | local function array_join(self, delimiter) 43 | if #self == 0 then 44 | return String() 45 | end 46 | local result = str(self:get(0)) 47 | delimiter = str(delimiter or "") 48 | for i = 1, #self - 1 do 49 | result = result .. delimiter .. self:get(i) 50 | end 51 | return result 52 | end 53 | 54 | local function array_next(self, index) 55 | index = index + 1 56 | if index > 0 and index <= #self then 57 | return index, self:get(index - 1) 58 | end 59 | end 60 | 61 | local function array_ipairs(self) 62 | return array_next, self, 0 63 | end 64 | 65 | local function array_generate__index(methods) 66 | return function(self, index) 67 | return methods[index] or array_safe_get(self, index - 1) 68 | end 69 | end 70 | 71 | local function array__newindex(self, index, value) 72 | array_safe_set(self, index - 1, value) 73 | end 74 | 75 | local function array__len(self) 76 | return self:size() 77 | end 78 | 79 | local function array__eq(a, b) 80 | if not has_length(a) or not has_length(b) or #a ~= #b then 81 | return false 82 | end 83 | for i = 1, #a do 84 | if a[i] ~= b[i] then 85 | return false 86 | end 87 | end 88 | return true 89 | end 90 | 91 | local function array_generate_get_buffer(ctype) 92 | local element_size = ffi_sizeof(ctype) 93 | return function(self) 94 | local buffer = PoolByteArray() 95 | local size = #self * element_size 96 | buffer:resize(size) 97 | local src = self:read() 98 | local dst = buffer:write() 99 | ffi_copy(dst:ptr(), src:ptr(), size) 100 | dst:destroy() 101 | src:destroy() 102 | return buffer 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /src/godot_class.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_class.lua Method Binds and generic Godot Class definitions 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Helper metatables for interfacing with Godot's OOP. 25 | -- None of them are available directly for creation, but are rather returned by 26 | -- the API. 27 | -- @module OOP 28 | 29 | -- Stringification helpers 30 | local function str(value) 31 | if ffi_istype(String, value) then 32 | return value 33 | else 34 | return Variant(value):as_string() 35 | end 36 | end 37 | 38 | local function gd_tostring(value) 39 | return tostring(str(value)) 40 | end 41 | 42 | local function concat_gdvalues(a, b) 43 | return ffi_gc(api.godot_string_operator_plus(str(a), str(b)), api.godot_string_destroy) 44 | end 45 | 46 | -- Class/Object definitions 47 | local ClassDB = api.godot_global_get_singleton("ClassDB") 48 | 49 | local Object_call = api.godot_method_bind_get_method('Object', 'call') 50 | local Object_get = api.godot_method_bind_get_method('Object', 'get') 51 | local Object_set = api.godot_method_bind_get_method('Object', 'set') 52 | local Object_has_method = api.godot_method_bind_get_method('Object', 'has_method') 53 | local Object_is_class = api.godot_method_bind_get_method('Object', 'is_class') 54 | local Reference_init_ref = api.godot_method_bind_get_method('Reference', 'init_ref') 55 | local Reference_reference = api.godot_method_bind_get_method('Reference', 'reference') 56 | local Reference_unreference = api.godot_method_bind_get_method('Reference', 'unreference') 57 | 58 | local function Object_gc(obj) 59 | if Reference_unreference(obj) then 60 | api.godot_object_destroy(obj) 61 | end 62 | end 63 | 64 | --- Wrapper for Godot Classes, used to create instances or index constants. 65 | -- These are constructed by `_G:__index` when indexing a known class name, e.g.: `KinematicBody`. 66 | -- @type ClassWrapper 67 | local class_methods = { 68 | --- Creates a new instance of a Class, initializing with the given values. 69 | -- If Class inherits from Reference, the reference is initialized with 70 | -- `init_ref` and object is marked for `unreference`ing at garbage-collection. 71 | -- @function new 72 | -- @param ... Parameters forwarded to a call to `_init` 73 | -- @treturn Object 74 | new = function(self, ...) 75 | local obj = self.constructor() 76 | if Object_is_class(obj, 'Reference') and Reference_init_ref(obj) then 77 | ffi_gc(obj, Object_gc) 78 | end 79 | obj:pcall('_init', ...) 80 | return obj 81 | end, 82 | --- Returns whether this Class inherits a `parent` Class. 83 | -- @function inherits 84 | -- @param other Other class name 85 | -- @treturn bool 86 | inherits = function(self, parent) 87 | return ClassDB:is_parent_class(self.class_name, parent) 88 | end, 89 | --- Returns the parent class. 90 | -- @function get_parent_class 91 | -- @treturn String 92 | get_parent_class = function(self) 93 | return ClassDB:get_parent_class(self.class_name) 94 | end, 95 | --- Returns whether class has a property named `name`. 96 | -- Only properties from `ClassDB:class_get_property_list()` return true. 97 | -- @function has_property 98 | -- @tparam string name Property name 99 | -- @treturn bool 100 | has_property = function(self, name) 101 | local cache = self.known_properties 102 | if not cache then 103 | cache = {} 104 | for _, prop in ipairs(ClassDB:class_get_property_list(self.class_name)) do 105 | cache[tostring(prop.name)] = true 106 | end 107 | self.known_properties = cache 108 | end 109 | return cache[name] ~= nil 110 | end, 111 | } 112 | local ClassWrapper = { 113 | new = function(self, class_name) 114 | return setmetatable({ 115 | --- (`string`) Class name 116 | class_name = class_name, 117 | --- (`Object (*)()`) Raw constructor function as returned by GDNative's `godot_get_class_constructor`. 118 | -- This is used by `new` and should probably not be used directly. 119 | -- @see new 120 | constructor = api.godot_get_class_constructor(class_name), 121 | }, self) 122 | end, 123 | --- Returns a `MethodBind` if class has a method with that name, or an integer 124 | -- constant if there is any. 125 | -- @function __index 126 | -- @param key 127 | -- @treturn[1] MethodBind If `ClassDB:class_has_method(self.class_name, key)` 128 | -- @treturn[2] int If `ClassDB:class_has_integer_constant(self.class_name, key)` 129 | -- @treturn[3] nil 130 | __index = function(self, key) 131 | local method = class_methods[key] 132 | if method then return method end 133 | local method_bind = api.godot_method_bind_get_method(self.class_name, key) 134 | if method_bind ~= nil then 135 | rawset(self, key, method_bind) 136 | return method_bind 137 | end 138 | local varkey = Variant(key) 139 | if ClassDB:class_has_integer_constant(self.class_name, varkey) then 140 | local constant = ClassDB:class_get_integer_constant(self.class_name, varkey) 141 | rawset(self, key, constant) 142 | return constant 143 | end 144 | end, 145 | --- Returns `class_name` 146 | -- @function __tostring 147 | -- @treturn string 148 | __tostring = function(self) 149 | return self.class_name 150 | end, 151 | } 152 | 153 | local function is_class_wrapper(v) 154 | return getmetatable(v) == ClassWrapper 155 | end 156 | 157 | 158 | local ClassWrapper_cache = setmetatable({}, { 159 | __index = function(self, class_name) 160 | if not ClassDB:class_exists(class_name) then 161 | return nil 162 | end 163 | class_name = tostring(class_name) 164 | local cls = ClassWrapper:new(class_name) 165 | rawset(self, class_name, cls) 166 | return cls 167 | end, 168 | }) 169 | 170 | 171 | --- MethodBind metatype, wrapper for `godot_method_bind`. 172 | -- These are returned by `ClassWrapper:__index` and GDNative's `godot_method_bind_get_method`. 173 | -- @type MethodBind 174 | local MethodBind = ffi_metatype('godot_method_bind', { 175 | --- Calls the method in `object`. 176 | -- @function __call 177 | -- @tparam Object object 178 | -- @param ... 179 | -- @return Method return value 180 | __call = function(self, obj, ...) 181 | local argc = select('#', ...) 182 | local argv = ffi_new('godot_variant *[?]', argc) 183 | for i = 1, argc do 184 | local arg = select(i, ...) 185 | argv[i - 1] = Variant(arg) 186 | end 187 | local r_error = ffi_new('godot_variant_call_error') 188 | local value = ffi_gc(api.godot_method_bind_call(self, _Object(obj), ffi_cast('const godot_variant **', argv), argc, r_error), api.godot_variant_destroy) 189 | if r_error.error == CallError.OK then 190 | return value:unbox() 191 | elseif r_error.error == CallError.ERROR_INVALID_METHOD then 192 | error("Invalid method") 193 | elseif r_error.error == CallError.ERROR_INVALID_ARGUMENT then 194 | error(string_format("Invalid argument #%d, expected %s", 195 | r_error.argument + 1, 196 | VariantType[tonumber(r_error.expected)] 197 | )) 198 | elseif r_error.error == CallError.ERROR_TOO_MANY_ARGUMENTS then 199 | error("Too many arguments") 200 | elseif r_error.error == CallError.ERROR_TOO_FEW_ARGUMENTS then 201 | error("Too few arguments") 202 | elseif r_error.error == CallError.ERROR_INSTANCE_IS_NULL then 203 | error("Instance is null") 204 | end 205 | end, 206 | }) 207 | 208 | 209 | --- A method binding object that calls a method by name, as `object:call(method_name, ...)`. 210 | -- These are returned by `Object.__index` if `object:has_method(method_name)` is true. 211 | -- @type MethodBindByName 212 | local MethodBindByName = { 213 | --- Create a new method binding by name. 214 | -- @usage 215 | -- local method_bind = MethodBindByName:new(method_name) 216 | -- method_bind(object, ...) 217 | -- @function new 218 | -- @tparam string method_name 219 | -- @treturn MethodBindByName 220 | new = function(self, method_name) 221 | return setmetatable({ 222 | --- (`string`) Method name. 223 | method_name = method_name 224 | }, self) 225 | end, 226 | --- Returns the result of `object:call(self.method_name, ...)`. 227 | -- @function __call 228 | -- @tparam Object|ScriptInstance|Variant object 229 | -- @param ... 230 | __call = function(self, obj, ...) 231 | return obj:call(self.method_name, ...) 232 | end, 233 | } 234 | 235 | -------------------------------------------------------------------------------- /src/godot_dictionary.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_dictionary.lua Wrapper for GDNative's Dictionary 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Dictionary metatype, wrapper for `godot_dictionary`. 25 | -- Construct using the idiom `Dictionary(from)`, which calls `__new`. 26 | -- @classmod Dictionary 27 | 28 | local Dictionary_erase = api_1_1 ~= nil and api_1_1.godot_dictionary_erase_with_return or api.godot_dictionary_erase 29 | 30 | local methods = { 31 | fillvariant = api.godot_variant_new_dictionary, 32 | varianttype = VariantType.Dictionary, 33 | 34 | --- Returns the number of keys in the Dictionary. 35 | -- @function size 36 | -- @treturn int 37 | size = api.godot_dictionary_size, 38 | --- Returns `true` if the Dictionary is empty. 39 | -- @function empty 40 | -- @treturn bool 41 | empty = api.godot_dictionary_empty, 42 | --- Clear the Dictionary, removing all key/value pairs. 43 | -- @function clear 44 | clear = api.godot_dictionary_clear, 45 | --- Returns `true` if the Dictionary has a given key. 46 | -- @function has 47 | -- @param key 48 | -- @treturn bool 49 | has = function(self, key) 50 | return api.godot_dictionary_has(self, Variant(key)) 51 | end, 52 | --- Returns `true` if the dictionary has all the given keys. 53 | -- @function has_all 54 | -- @param ... If only a table or Array value is passed, its values are used as search keys. 55 | -- Otherwise, all passed arguments will be used as search keys. 56 | -- @treturn bool 57 | has_all = function(self, keys, ...) 58 | if select('#', ...) > 0 or not ffi_istype(Array, keys) then 59 | keys = Array(keys, ...) 60 | end 61 | return api.godot_dictionary_has_all(self, keys) 62 | end, 63 | --- Erase a dictionary key/value pair by key. 64 | -- Returns `true` if the given key was present in the Dictionary, `false` otherwise. 65 | -- Does not erase elements while iterating over the dictionary. 66 | -- @function erase 67 | -- @param key Key, boxed with `Variant.__new` 68 | -- @treturn bool 69 | erase = function(self, key) 70 | return Dictionary_erase(self, Variant(key)) 71 | end, 72 | --- Returns a hashed integer value representing the Dictionary contents. 73 | -- This can be used to compare Dictionaries by value. 74 | -- Note: Dictionaries with the same keys/values but in a different order will have a different hash. 75 | -- @function hash 76 | -- @treturn int 77 | hash = api.godot_dictionary_hash, 78 | --- Returns the list of keys in the Dictionary. 79 | -- @function keys 80 | -- @treturn Array 81 | keys = function(self) 82 | return ffi_gc(api.godot_dictionary_keys(self), api.godot_array_destroy) 83 | end, 84 | --- Returns the list of values in the Dictionary. 85 | -- @function values 86 | -- @treturn Array 87 | values = function(self) 88 | return ffi_gc(api.godot_dictionary_values(self), api.godot_array_destroy) 89 | end, 90 | --- Returns the current value for the specified `key` in the Dictionary. 91 | -- If the key does not exist, the method returns the value of the optional `default` argument, or `nil` if it is omitted. 92 | -- @function get 93 | -- @param key 94 | -- @param[opt] default Default value to be returned if key doesn't exist in Dictionary 95 | -- @return Unboxed value or `default` or nil 96 | get = function(self, key, default) 97 | if default ~= nil and api_1_1 ~= nil then 98 | return ffi_gc(api_1_1.godot_dictionary_get_with_default(self, Variant(key), Variant(default)), api.godot_variant_destroy):unbox() 99 | else 100 | return ffi_gc(api.godot_dictionary_get(self, Variant(key)), api.godot_variant_destroy):unbox() 101 | end 102 | end, 103 | --- Set a new value for the specified `key` in the Dictionary. 104 | -- @function set 105 | -- @param key 106 | -- @param value 107 | set = function(self, key, value) 108 | api.godot_dictionary_set(self, Variant(key), Variant(value)) 109 | end, 110 | --- Returns the next key/value pair Dictionary's, similar to Lua's [next](https://www.lua.org/manual/5.1/manual.html#pdf-next). 111 | -- This is used to iterate over Dictionaries in `__pairs`. 112 | -- @usage 113 | -- local key, value = nil -- First key being `nil`, the iteration begins 114 | -- while true do 115 | -- key, value = dict:next(key) 116 | -- if key == nil then break end 117 | -- -- do something 118 | -- end 119 | -- @function next 120 | -- @param key If `nil`, returns the first key/value pair. 121 | -- Otherwise, returns the next key/value pair. 122 | -- @return[1] Key 123 | -- @return[1] Value 124 | -- @treturn[2] nil If there are no more keys 125 | -- @see __pairs 126 | next = function(self, key) 127 | if key ~= nil then 128 | key = Variant(key) 129 | end 130 | local next_key = api.godot_dictionary_next(self, key) 131 | if next_key ~= nil then 132 | return next_key:unbox(), self:get(next_key) 133 | else 134 | return nil 135 | end 136 | end, 137 | --- Returns a String with JSON representation of the Dictionary's contents. 138 | -- @function to_json 139 | -- @treturn String 140 | to_json = function(self) 141 | return ffi_gc(api.godot_dictionary_to_json(self), api.godot_string_destroy) 142 | end, 143 | } 144 | 145 | if api_1_2 ~= nil then 146 | --- Creates a copy of the dictionary, and returns it. 147 | -- The `deep` parameter causes inner Dictionaries and Arrays to be copied recursively, but does not apply to Objects. 148 | -- @function duplicate 149 | -- @param[opt=false] deep 150 | -- @treturn Dictionary 151 | methods.duplicate = function(self, deep) 152 | return ffi_gc(api_1_2.godot_dictionary_duplicate(self, deep or false), api.godot_dictionary_destroy) 153 | end 154 | end 155 | 156 | if api_1_3 ~= nil then 157 | --- Adds elements from `dictionary` to this Dictionary. 158 | -- By default, duplicate keys will not be copied over, unless `overwrite` is `true`. 159 | -- @function merge 160 | -- @tparam Dictionary dictionary 161 | -- @param[opt=false] overwrite 162 | methods.merge = function(self, dictionary, overwrite) 163 | api_1_3.godot_dictionary_merge(self, dictionary, overwrite or false) 164 | end 165 | end 166 | 167 | Dictionary = ffi_metatype('godot_dictionary', { 168 | --- Dictionary constructor, called by the idiom `Dictionary(value)`. 169 | -- @function __new 170 | -- @param[opt] value If passed, the key/value pairs will be iterated with 171 | -- `pairs` and inserted into the new Dictionary using `set`. 172 | -- Notice that both tables and userdata/cdata with a `__pairs` metamethod 173 | -- are valid, including another Dictionary. 174 | -- @treturn Dictionary 175 | __new = function(mt, value) 176 | local self = ffi_new('godot_dictionary') 177 | api.godot_dictionary_new(self) 178 | if value then 179 | for k, v in pairs(value) do 180 | self[k] = v 181 | end 182 | end 183 | return self 184 | end, 185 | __index = function(self, key) 186 | return methods[key] or methods.get(self, key) 187 | end, 188 | --- Set a value to Dictionary. 189 | -- If `value` is `nil`, the `key` will be `erase`d, similarly to Lua tables. 190 | -- To insert a Nil Variant into Dictionary, use `set` instead. 191 | -- @function __newindex 192 | -- @param key 193 | -- @param value 194 | __newindex = function(self, key, value) 195 | if type(value) == 'nil' then 196 | api.godot_dictionary_erase(self, Variant(key)) 197 | else 198 | api.godot_dictionary_set(self, Variant(key), Variant(value)) 199 | end 200 | end, 201 | __gc = api.godot_dictionary_destroy, 202 | --- Returns a Lua string representation of this Dictionary. 203 | -- @function __tostring 204 | -- @treturn string 205 | __tostring = gd_tostring, 206 | --- Concatenates values. 207 | -- @function __concat 208 | -- @param a First value, stringified with `GD.str` 209 | -- @param b First value, stringified with `GD.str` 210 | -- @treturn String 211 | __concat = concat_gdvalues, 212 | --- Alias for `size`. 213 | -- @function __len 214 | -- @treturn int 215 | -- @see size 216 | __len = function(self) 217 | return methods.size(self) 218 | end, 219 | --- Returns the `next` iterator and `self`, called by the idiom `pairs(dictionary)`. 220 | -- @usage 221 | -- for k, v in pairs(dictionary) do 222 | -- -- do something 223 | -- end 224 | -- @function __pairs 225 | -- @treturn function 226 | -- @treturn Dictionary self 227 | __pairs = function(self) 228 | return methods.next, self 229 | end, 230 | }) 231 | 232 | -------------------------------------------------------------------------------- /src/godot_node_path.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_node_path.lua Wrapper for GDNative's NodePath 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- NodePath metatype, wrapper for `godot_node_path`. 25 | -- Construct using the idiom `NodePath(path)`, which calls `__new`. 26 | -- @classmod NodePath 27 | 28 | local function is_a_node_path(v) 29 | return is_a_string(v) or ffi_istype(NodePath, v) 30 | end 31 | 32 | local methods = { 33 | fillvariant = api.godot_variant_new_node_path, 34 | varianttype = VariantType.NodePath, 35 | 36 | --- Gets a `String` that represents this NodePath. 37 | -- @function as_string 38 | -- @treturn String 39 | as_string = function(self) 40 | return ffi_gc(api.godot_node_path_as_string(self), api.godot_string_destroy) 41 | end, 42 | --- Returns `true` if the node path is absolute (as opposed to relative), which means that it starts with a slash character (`/`). 43 | -- @function is_absolute 44 | -- @treturn bool 45 | is_absolute = api.godot_node_path_is_absolute, 46 | --- Gets the number of node names which make up the path. 47 | -- Subnames (see `get_subname_count`) are not included. 48 | -- @function get_name_count 49 | -- @treturn int 50 | get_name_count = api.godot_node_path_get_name_count, 51 | --- Gets the node name indicated by `idx`. 52 | -- @function get_name 53 | -- @tparam int idx 54 | -- @treturn String 55 | get_name = function(self, idx) 56 | return ffi_gc(api.godot_node_path_get_name(self, idx), api.godot_string_destroy) 57 | end, 58 | --- Gets the number of resource or property names ("subnames") in the path. 59 | -- Each subname is listed after a colon character (`:`) in the node path. 60 | -- @function get_subname_count 61 | -- @treturn int 62 | get_subname_count = api.godot_node_path_get_subname_count, 63 | --- Gets the resource or property name indicated by `idx`. 64 | -- @function get_subname 65 | -- @tparam int idx 66 | -- @treturn String 67 | get_subname = function(self, idx) 68 | return ffi_gc(api.godot_node_path_get_subname(self, idx), api.godot_string_destroy) 69 | end, 70 | --- Returns all subnames concatenated with a colon character (`:`) as separator, i.e. the right side of the first colon in a node path. 71 | -- @function get_concatenated_subnames 72 | -- @treturn String 73 | get_concatenated_subnames = function(self) 74 | return ffi_gc(api.godot_node_path_get_concatenated_subnames(self), api.godot_string_destroy) 75 | end, 76 | --- Returns `true` if the node path is empty. 77 | -- @function is_empty 78 | -- @treturn bool 79 | is_empty = api.godot_node_path_is_empty, 80 | --- Returns a node path with a colon character (`:`) prepended, transforming it to a pure property path with no node name (defaults to resolving from the current node). 81 | -- @function get_as_property_path 82 | -- @treturn String 83 | get_as_property_path = function(self) 84 | return ffi_gc(api.godot_node_path_get_as_property_path(self), api.godot_node_path_destroy) 85 | end, 86 | } 87 | NodePath = ffi_metatype('godot_node_path', { 88 | --- NodePath constructor, called by the idiom `NodePath(path)`. 89 | -- 90 | -- * `NodePath()`: empty NodePath 91 | -- * `NodePath(any path)`: created with `path` stringified with `GD.str` 92 | -- * `NodePath(NodePath other)`: copy from `other` 93 | -- @function __new 94 | -- @param[opt] path 95 | -- @treturn NodePath 96 | __new = function(mt, path) 97 | local self = ffi_new(mt) 98 | if ffi_istype(mt, path) then 99 | api.godot_node_path_new_copy(self, path) 100 | else 101 | api.godot_node_path_new(self, str(path or '')) 102 | end 103 | return self 104 | end, 105 | __gc = api.godot_node_path_destroy, 106 | __index = methods, 107 | --- Returns a Lua string representation of this NodePath. 108 | -- @function __tostring 109 | -- @treturn string 110 | -- @see as_string 111 | __tostring = function(self) 112 | return tostring(methods.as_string(self)) 113 | end, 114 | --- Concatenates values. 115 | -- @function __concat 116 | -- @param a First value, stringified with `GD.str` 117 | -- @param b First value, stringified with `GD.str` 118 | -- @treturn String 119 | __concat = concat_gdvalues, 120 | --- Equality comparison (`a == b`). 121 | -- If either `a` or `b` are not of type `NodePath`, `String` or Lua `string`, always return `false`. 122 | -- @function __eq 123 | -- @param a 124 | -- @param b 125 | -- @treturn bool 126 | __eq = function(a, b) 127 | return is_a_node_path(a) and is_a_node_path(b) and api.godot_node_path_operator_equal(NodePath(a), NodePath(b)) 128 | end, 129 | }) 130 | -------------------------------------------------------------------------------- /src/godot_plane.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_plane.lua Wrapper for GDNative's Plane 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Plane metatype, wrapper for `godot_plane`. 25 | -- Constructed using the idiom `Plane(...)`, which calls `__new`. 26 | -- 27 | -- The components may be accessed through `elements` or individually as 28 | -- `normal/d`: 29 | -- typedef union godot_plane { 30 | -- float elements[4]; 31 | -- struct { Vector3 normal; float d; }; 32 | -- } godot_plane; 33 | -- @classmod Plane 34 | 35 | local methods = { 36 | fillvariant = api.godot_variant_new_plane, 37 | varianttype = VariantType.Plane, 38 | 39 | --- Returns a copy of the plane, normalized. 40 | -- @function normalized 41 | -- @treturn Plane 42 | normalized = api.godot_plane_normalized, 43 | --- Returns the center of the plane. 44 | -- @function center 45 | -- @treturn Vector3 46 | center = api.godot_plane_center, 47 | --- Returns `true` if `point` is located above the plane. 48 | -- @function is_point_over 49 | -- @tparam Vector2 point 50 | -- @treturn bool 51 | is_point_over = api.godot_plane_is_point_over, 52 | --- Returns the shortest distance from the plane to the position `point`. 53 | -- @function distance_to 54 | -- @tparam Vector3 point 55 | -- @treturn number 56 | distance_to = api.godot_plane_distance_to, 57 | --- Returns `true` if `point` is inside the plane. 58 | -- Comparison uses a custom minimum `epsilon` threshold. 59 | -- @function has_point 60 | -- @tparam Vector3 point 61 | -- @tparam[opt=1e-5] number epsilon 62 | -- @treturn bool 63 | has_point = function(self, point, epsilon) 64 | return api.godot_plane_has_point(self, point, epsilon or 1e-5) 65 | end, 66 | --- Returns the orthogonal projection of `point` into a point in the plane. 67 | -- @function project 68 | -- @tparam Vector3 point 69 | -- @treturn Vector3 70 | project = api.godot_plane_project, 71 | --- Returns the intersection point of the three planes `b`, `c` and this plane. 72 | -- @function intersect_3 73 | -- @tparam Plane b 74 | -- @tparam Plane c 75 | -- @treturn[1] Vector3 Intersection point, if there is any 76 | -- @treturn[2] nil If no intersection is found 77 | intersect_3 = function(self, b, c) 78 | local dest = Vector3() 79 | if api.godot_plane_intersect_3(self, dest, b, c) then 80 | return dest 81 | else 82 | return nil 83 | end 84 | end, 85 | --- Returns the intersection point of a ray consisting of the position `from` and the direction normal `dir` with this plane. 86 | -- @function intersects_ray 87 | -- @tparam Vector3 from 88 | -- @tparam Vector3 dir 89 | -- @treturn[1] Vector3 Intersection point, if there is any 90 | -- @treturn[2] nil If no intersection is found 91 | intersects_ray = function(self, from, dir) 92 | local dest = Vector3() 93 | if api.godot_plane_intersects_ray(self, dest, from, dir) then 94 | return dest 95 | else 96 | return nil 97 | end 98 | end, 99 | --- Returns the intersection point of a segment from position `begin` to position `end` with this plane. 100 | -- @function intersects_segment 101 | -- @tparam Vector3 begin 102 | -- @tparam Vector3 end 103 | -- @treturn[1] Vector3 Intersection point, if there is any 104 | -- @treturn[2] nil If no intersection is found 105 | intersects_segment = function(self, begin, end_) 106 | local dest = Vector3() 107 | if api.godot_plane_intersects_segment(self, dest, begin, end_) then 108 | return dest 109 | else 110 | return nil 111 | end 112 | end, 113 | } 114 | 115 | --- Returns all elements. 116 | -- @function unpack 117 | -- @treturn number normal.x 118 | -- @treturn number normal.y 119 | -- @treturn number normal.z 120 | -- @treturn number d 121 | methods.unpack = function(self) 122 | return self.normal.x, self.normal.y, self.normal.z, self.d 123 | end 124 | 125 | --- Constants 126 | -- @section constants 127 | 128 | --- A plane that extends in the Y and Z axes (normal vector points +X). 129 | -- @field YZ Plane(1, 0, 0, 0) 130 | 131 | --- A plane that extends in the X and Z axes (normal vector points +Y). 132 | -- @field XZ Plane(0, 1, 0, 0) 133 | 134 | --- A plane that extends in the X and Y axes (normal vector points +Z). 135 | -- @field XY Plane(0, 0, 1, 0) 136 | 137 | --- @section end 138 | 139 | methods.YZ = ffi_new('godot_plane', { elements = { 1, 0, 0, 0 } }) 140 | methods.XZ = ffi_new('godot_plane', { elements = { 0, 1, 0, 0 } }) 141 | methods.XY = ffi_new('godot_plane', { elements = { 0, 0, 1, 0 } }) 142 | 143 | --- Metamethods 144 | -- @section metamethods 145 | Plane = ffi_metatype('godot_plane', { 146 | --- Plane constructor, called by the idiom `Plane(...)`. 147 | -- 148 | -- * `Plane(Vector3 normal, number d)`: set the normal and the plane's distance to the origin 149 | -- * `Plane(number a, number b, number c, number d)`: normal is set to `Vector3(a, b, c)`, distance is set to `d` 150 | -- * `Plane(Vector3 a, Vector3 b, Vector3 c)`: creates a plane from the three points, given in clockwise order 151 | -- * `Plane(Plane other)`: copy values from `other` 152 | -- @function __new 153 | -- @param ... 154 | -- @treturn Plane 155 | __new = function(mt, a, b, c, d) 156 | if ffi_istype(mt, a) then 157 | return ffi_new(mt, a) 158 | end 159 | local self = ffi_new(mt) 160 | if ffi_istype(Vector3, a) then 161 | if ffi.istype(Vector3, b) then 162 | api.godot_plane_new_with_vectors(self, a, b, c) 163 | else 164 | api.godot_plane_new_with_normal(self, a, b) 165 | end 166 | else 167 | api.godot_plane_new_with_reals(self, a, b, c, d) 168 | end 169 | return self 170 | end, 171 | __index = methods, 172 | --- Returns a Lua string representation of this plane. 173 | -- @function __tostring 174 | -- @treturn string 175 | __tostring = gd_tostring, 176 | --- Concatenates values. 177 | -- @function __concat 178 | -- @param a First value, stringified with `GD.str` 179 | -- @param b First value, stringified with `GD.str` 180 | -- @treturn String 181 | __concat = concat_gdvalues, 182 | --- Unary minus operation 183 | -- @function __unm 184 | -- @treturn Plane 185 | __unm = function(self) 186 | return Plane(-self.normal, -self.d) 187 | end, 188 | --- Equality operation 189 | -- If either `a` or `b` are not of type `Plane`, always return `false`. 190 | -- @function __eq 191 | -- @tparam Plane a 192 | -- @tparam Plane b 193 | -- @treturn bool 194 | __eq = function(a, b) 195 | return a.d == b.d and a.normal == b.normal 196 | end, 197 | }) 198 | 199 | 200 | -------------------------------------------------------------------------------- /src/godot_quat.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_quat.lua Wrapper for GDNative's Quat 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Quat metatype, wrapper for `godot_quat`. 25 | -- Constructed using the idiom `Quat(...)`, which calls `__new`. 26 | -- 27 | -- The X, Y, Z and W components may be accessed through `elements` or individually 28 | -- with `x/y/z/w`. `Vector2` with two adjacent components may be get/set with the 29 | -- pairs `xy/yz/zw`. `Vector3` with three adjacent components may be get/set with 30 | -- the triplets `xyz/yzw`: 31 | -- typedef union godot_quat { 32 | -- float elements[4]; 33 | -- struct { float x, y, z, w; }; 34 | -- struct { Vector2 xy; Vector2 zw; }; 35 | -- struct { float _; Vector2 yz; float _; }; 36 | -- struct { Vector3 xyz; float _; }; 37 | -- struct { float _; Vector3 yzw; }; 38 | -- } godot_quat; 39 | -- @classmod Quat 40 | 41 | local methods = { 42 | fillvariant = api.godot_variant_new_quat, 43 | varianttype = VariantType.Quat, 44 | 45 | --- Returns the length of the quaternion. 46 | -- @function length 47 | -- @treturn number 48 | length = api.godot_quat_length, 49 | --- Returns the length of the quaternion, squared. 50 | -- @function length_squared 51 | -- @treturn number 52 | length_squared = api.godot_quat_length_squared, 53 | --- Returns a copy of the quaternion, normalized to unit length. 54 | -- @function normalized 55 | -- @treturn Quat 56 | normalized = api.godot_quat_normalized, 57 | --- Returns whether the quaternion is normalized or not. 58 | -- @function is_normalized 59 | -- @treturn bool 60 | is_normalized = api.godot_quat_is_normalized, 61 | --- Returns the inverse of the quaternion. 62 | -- @function inverse 63 | -- @treturn Quat 64 | inverse = api.godot_quat_inverse, 65 | --- Returns the dot product of two quaternions. 66 | -- @function dot 67 | -- @tparam Quat b 68 | -- @treturn number 69 | dot = api.godot_quat_dot, 70 | --- Returns a vector transformed (multiplied) by this quaternion. 71 | -- @function xform 72 | -- @tparam Vector3 v 73 | -- @treturn Vector3 74 | xform = api.godot_quat_xform, 75 | --- Returns the result of the spherical linear interpolation between this quaternion and `to` by amount `weight`. 76 | -- Note: Both quaternions must be normalized. 77 | -- @function slerp 78 | -- @tparam Quat to 79 | -- @tparam number weight 80 | -- @treturn Quat 81 | -- @see slerpni 82 | slerp = api.godot_quat_slerp, 83 | --- Returns the result of the spherical linear interpolation between this quaternion and `to` by amount `weight`, but without checking if the rotation path is not bigger than 90 degrees. 84 | -- Note: Both quaternions must be normalized. 85 | -- @function slerpni 86 | -- @tparam Quat to 87 | -- @tparam number weight 88 | -- @treturn Quat 89 | -- @see slerp 90 | slerpni = api.godot_quat_slerpni, 91 | --- Performs a cubic spherical interpolation between quaternions `preA`, this vector, `b`, and `postB`, by the given amount `t`. 92 | -- @function cubic_slerp 93 | -- @tparam Quat b 94 | -- @tparam Quat preA 95 | -- @tparam Quat postB 96 | -- @tparam number t 97 | -- @treturn Quat 98 | cubic_slerp = api.godot_quat_cubic_slerp, 99 | } 100 | 101 | --- Returns all elements. 102 | -- @function unpack 103 | -- @treturn number X 104 | -- @treturn number Y 105 | -- @treturn number Z 106 | -- @treturn number W 107 | methods.unpack = function(self) 108 | return self.x, self.y, self.z, self.w 109 | end 110 | 111 | --- Constants 112 | -- @section constants 113 | 114 | --- The identity quaternion, representing no rotation. 115 | -- Equivalent to an identity `Basis` matrix. If a vector is transformed by an identity quaternion, it will not change. 116 | -- @field IDENTITY Quat(0, 0, 0, 1) 117 | 118 | --- @section end 119 | 120 | methods.IDENTITY = ffi_new('godot_quat', { elements = { 0, 0, 0, 1 } }) 121 | 122 | --- Metamethods 123 | -- @section metamethods 124 | Quat = ffi_metatype('godot_quat', { 125 | --- Quat constructor, called by the idiom `Quat(...)`. 126 | -- 127 | -- * `Quat()`: `IDENTITY` quaternion (`Quat() == Quat(0, 0, 0, 1)`) 128 | -- * `Quat(number x[, number y = 0[, number z = 0[, number w = 1]]])`: set XYZW 129 | -- * `Quat(Basis basis)`: construct from basis 130 | -- * `Quat(Vector3 euler)`: rotation specified by Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last) 131 | -- * `Quat(Vector3 axis, number angle)`: rotates around the given axis by the specified angle. The axis must be a normalized vector. 132 | -- * `Quat(Quat other)`: copy values from `other` 133 | -- @function __new 134 | -- @param ... 135 | -- @treturn Quat 136 | __new = function(mt, x, y, z, w) 137 | if ffi_istype(mt, x) then 138 | return ffi_new(mt, x) 139 | end 140 | local self = ffi_new(mt) 141 | if ffi_istype(Vector3, x) then 142 | if y then 143 | api.godot_quat_new_with_axis_angle(self, x, y) 144 | else 145 | api_1_1.godot_quat_new_with_euler(self, x) 146 | end 147 | elseif ffi_istype(Basis, x) then 148 | api_1_1.godot_quat_new_with_basis(self, x) 149 | else 150 | api.godot_quat_new(self, x or 0, y or 0, z or 0, w or 1) 151 | end 152 | return self 153 | end, 154 | __index = methods, 155 | --- Returns a Lua string representation of this quaternion. 156 | -- @function __tostring 157 | -- @treturn string 158 | __tostring = gd_tostring, 159 | --- Concatenates values. 160 | -- @function __concat 161 | -- @param a First value, stringified with `GD.str` 162 | -- @param b First value, stringified with `GD.str` 163 | -- @treturn String 164 | __concat = concat_gdvalues, 165 | --- Addition operation 166 | -- @function __add 167 | -- @tparam Quat a 168 | -- @tparam Quat b 169 | -- @treturn Quat 170 | __add = function(a, b) 171 | return Quat(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w) 172 | end, 173 | --- Subtraction operation 174 | -- @function __sub 175 | -- @tparam Quat a 176 | -- @tparam Quat b 177 | -- @treturn Quat 178 | __sub = function(a, b) 179 | return Quat(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w) 180 | end, 181 | --- Multiplication operation 182 | -- @function __mul 183 | -- @tparam Quat a 184 | -- @tparam Vector3|number b If a Vector3 is passed, calls `xform`. 185 | -- Otherwise, returns a Quat with each component multiplied by `b`. 186 | -- @treturn Quat|Vector3 187 | __mul = function(self, b) 188 | if ffi_istype(Vector3, b) then 189 | return self:xform(b) 190 | else 191 | return Quat(self.x * b, self.y * b, self.z * b, self.w * b) 192 | end 193 | end, 194 | --- Division operation 195 | -- @function __div 196 | -- @tparam Quat a 197 | -- @tparam number s 198 | -- @treturn Quat 199 | __div = function(self, s) 200 | return self * (1.0 / s) 201 | end, 202 | --- Unary minus operation 203 | -- @function __unm 204 | -- @treturn Quat 205 | __unm = function(self) 206 | return Quat(-self.x, -self.y, -self.z, -self.w) 207 | end, 208 | --- Equality operation 209 | -- If either `a` or `b` are not of type `Quat`, always return `false`. 210 | -- @function __eq 211 | -- @tparam Quat a 212 | -- @tparam Quat b 213 | -- @treturn bool 214 | __eq = function(a, b) 215 | return ffi_istype(Quat, a) and ffi_istype(Quat, b) and a.x == b.x and a.y == b.y and a.z == b.z and a.w == b.w 216 | end, 217 | }) 218 | 219 | 220 | -------------------------------------------------------------------------------- /src/godot_rect2.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_rect2.lua Wrapper for GDNative's Rect2 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Color metatype, wrapper for `godot_rect2`. 25 | -- Construct using the idiom `Rect2(...)`, which calls `__new`. 26 | -- 27 | -- Components may be accessed through `elements`, individually using 28 | -- `x/y/width/height` or as `Vector2` `position/size`: 29 | -- typedef union godot_rect2 { 30 | -- uint8_t data[16]; 31 | -- float elements[4]; 32 | -- struct { float x, y, width, height; }; 33 | -- struct { Vector2 position; Vector2 size; }; 34 | -- } godot_rect2; 35 | -- @classmod Rect2 36 | 37 | local methods = { 38 | fillvariant = api.godot_variant_new_rect2, 39 | varianttype = VariantType.Rect2, 40 | 41 | --- Returns the area of the Rect2. 42 | -- @function get_area 43 | -- @treturn number 44 | get_area = api.godot_rect2_get_area, 45 | --- Returns `true` if the Rect2 overlaps with `b`. (i.e. they have at least one point in common). 46 | -- @function intersects 47 | -- @tparam Rect2 b 48 | -- @param[opt=false] include_borders If `true`, they will also be considered overlapping if their borders touch, even without intersection. 49 | -- @treturn bool 50 | intersects = function(self, b, include_borders) 51 | return api.godot_rect2_intersects(self, b, include_borders or false) 52 | end, 53 | --- Returns `true` if this Rect2 completely encloses another one. 54 | -- @function encloses 55 | -- @tparam Rect2 b 56 | -- @treturn bool 57 | encloses = api.godot_rect2_encloses, 58 | --- Returns `true` if the Rect2 is flat or empty. 59 | -- @function has_no_area 60 | -- @treturn bool 61 | has_no_area = api.godot_rect2_has_no_area, 62 | --- Returns the intersection of this Rect2 and `b`. 63 | -- @function clip 64 | -- @tparam Rect2 b 65 | -- @treturn Rect2 66 | clip = api.godot_rect2_clip, 67 | --- Returns a larger Rect2 that contains this Rect2 and `b`. 68 | -- @function merge 69 | -- @tparam Rect2 b 70 | -- @treturn Rect2 71 | merge = api.godot_rect2_merge, 72 | --- Returns `true` if the Rect2 contains a point. 73 | -- @function has_point 74 | -- @tparam Vector2 point 75 | -- @treturn bool 76 | has_point = api.godot_rect2_has_point, 77 | --- Returns a copy of the Rect2 grown a given `amount` of units towards all the sides. 78 | -- @function grow 79 | -- @tparam number amount 80 | -- @treturn Rect2 81 | grow = api.godot_rect2_grow, 82 | --- Returns this Rect2 expanded to include a given point. 83 | -- @function expand 84 | -- @tparam Vector2 point 85 | -- @treturn Rect2 86 | expand = api.godot_rect2_expand, 87 | } 88 | 89 | --- Returns all elements. 90 | -- @function unpack 91 | -- @treturn number X 92 | -- @treturn number Y 93 | -- @treturn number width 94 | -- @treturn number height 95 | methods.unpack = function(self) 96 | return self.x, self.y, self.width, self.height 97 | end 98 | 99 | if api_1_1 then 100 | --- Returns a copy of the Rect2 grown a given amount of units towards each direction individually. 101 | -- @function grow_individual 102 | -- @tparam number left 103 | -- @tparam number top 104 | -- @tparam number right 105 | -- @tparam number bottom 106 | -- @treturn Rect2 107 | methods.grow_individual = api_1_1.godot_rect2_grow_individual 108 | --- Returns a copy of the Rect2 grown a given amount of units towards the Margin direction. 109 | -- @function grow_margin 110 | -- @tparam int margin 111 | -- @tparam number amount 112 | -- @treturn Rect2 113 | methods.grow_margin = api_1_1.godot_rect2_grow_margin 114 | --- Returns a Rect2 with equivalent position and area, modified so that the top-left corner is the origin and `width` and `height` are positive. 115 | -- @function abs 116 | -- @treturn Rect2 117 | methods.abs = api_1_1.godot_rect2_abs 118 | end 119 | 120 | Rect2 = ffi_metatype('godot_rect2', { 121 | --- Rect2 constructor, called by the idiom `Rect2(...)`. 122 | -- 123 | -- * `Rect2()`: all zeros (`Rect2() == Rect2(0, 0, 0, 0)`) 124 | -- * `Rect2(number x, number y, number width, number height)`: set XY and width/height 125 | -- * `Rect2(Vector2 position, Vector2 size)`: set position and size 126 | -- * `Rect2(Rect2 other)`: copy values from `other` 127 | -- @function __new 128 | -- @param ... 129 | -- @treturn Rect2 130 | __new = function(mt, x, y, width, height) 131 | if ffi_istype(mt, x) then 132 | return ffi_new(mt, x) 133 | elseif ffi_istype(Vector2, x) then 134 | -- (Vector2, Vector2) 135 | if ffi_istype(Vector2, y) then 136 | x, y, width, height = x.x, x.y, y.x, y.y 137 | -- (Vector2, float?, float?) 138 | else 139 | x, y, width, height = x.x, x.y, y, width 140 | end 141 | -- (float, float, Vector2) 142 | elseif ffi_istype(Vector2, width) then 143 | x, y, width, height = x, y, width.x, width.y 144 | end 145 | return ffi_new(mt, { x = x, y = y, width = width, height = height }) 146 | end, 147 | __index = methods, 148 | --- Returns a Lua string representation of this rect. 149 | -- @function __tostring 150 | -- @treturn string 151 | __tostring = gd_tostring, 152 | --- Concatenates values. 153 | -- @function __concat 154 | -- @param a First value, stringified with `GD.str` 155 | -- @param b First value, stringified with `GD.str` 156 | -- @treturn String 157 | __concat = concat_gdvalues, 158 | --- Equality operation 159 | -- @function __eq 160 | -- @tparam Rect2 a 161 | -- @tparam Rect2 b 162 | -- @treturn bool 163 | __eq = function(a, b) 164 | return a.x == b.x and a.y == b.y and a.width == b.width and a.height == b.height 165 | end, 166 | }) 167 | 168 | 169 | -------------------------------------------------------------------------------- /src/godot_rid.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_rid.lua Wrapper for GDNative's RID 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- RID metatype, wrapper for `godot_rid`. 25 | -- Construct using the idiom `RID(from)`, which calls `__new`. 26 | -- @classmod RID 27 | 28 | local methods = { 29 | fillvariant = api.godot_variant_new_rid, 30 | varianttype = VariantType.RID, 31 | 32 | --- Returns the ID of the referenced resource. 33 | -- @function get_id 34 | -- @treturn int 35 | get_id = api.godot_rid_get_id, 36 | } 37 | RID = ffi_metatype('godot_rid', { 38 | --- RID constructor, called by the idiom `RID(from)`. 39 | -- @function __new 40 | -- @param[opt] from 41 | -- @treturn RID 42 | __new = function(mt, from) 43 | local self = ffi_new(mt) 44 | if from then 45 | api.godot_rid_new_with_resource(self, from) 46 | else 47 | api.godot_rid_new(self) 48 | end 49 | return self 50 | end, 51 | __index = methods, 52 | --- Returns a Lua string representation of this RID. 53 | -- @function __tostring 54 | -- @treturn string 55 | __tostring = gd_tostring, 56 | --- Concatenates values. 57 | -- @function __concat 58 | -- @param a First value, stringified with `GD.str` 59 | -- @param b First value, stringified with `GD.str` 60 | -- @treturn String 61 | __concat = concat_gdvalues, 62 | --- Equality comparison (`a == b`). 63 | -- If either `a` or `b` are not RIDs, always return `false`. 64 | -- @function __eq 65 | -- @param a 66 | -- @param b 67 | -- @treturn bool 68 | __eq = function(a, b) 69 | return ffi_istype(RID, a) and ffi_istype(RID, b) and api.godot_rid_operator_equal(a, b) 70 | end, 71 | --- Less than comparison (`a < b`). 72 | -- @function __lt 73 | -- @param a 74 | -- @param b 75 | -- @treturn bool 76 | __lt = api.godot_rid_operator_less, 77 | }) 78 | 79 | -------------------------------------------------------------------------------- /src/godot_string_name.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_string_name.lua Wrapper for GDNative's StringName 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- StringName metatype, wrapper for `godot_string_name`. 25 | -- Construct using the idiom `StringName(name)`, which calls `__new`. 26 | -- @classmod StringName 27 | 28 | local methods = { 29 | fillvariant = function(var, self) 30 | api.godot_variant_new_string(var, self:get_name()) 31 | end, 32 | varianttype = VariantType.String, 33 | 34 | --- Returns this StringName as a `String`. 35 | -- @function get_name 36 | -- @treturn String 37 | get_name = function(self) 38 | return ffi_gc(api.godot_string_name_get_name(self), api.godot_string_destroy) 39 | end, 40 | --- Returns this StringName's 32-bit hash value. 41 | -- @function get_hash 42 | -- @treturn uint32_t 43 | get_hash = api.godot_string_name_get_hash, 44 | --- Returns this StringName's data pointer (`const void *`) 45 | -- @function get_data_unique_pointer 46 | -- @return[type=const void *] 47 | get_data_unique_pointer = api.godot_string_name_get_data_unique_pointer, 48 | } 49 | StringName = ffi_metatype('godot_string_name', { 50 | --- StringName constructor, called by the idiom `StringName(name)`. 51 | -- @function __new 52 | -- @param[opt=""] name Name, stringified with `GD.str` 53 | -- @treturn StringName 54 | __new = function(mt, name) 55 | local self = ffi_new(mt) 56 | api.godot_string_name_new(self, str(name or '')) 57 | return self 58 | end, 59 | __gc = api.godot_string_name_destroy, 60 | --- Returns a Lua string representation of this StringName. 61 | -- @function __tostring 62 | -- @treturn string 63 | -- @see get_name 64 | __tostring = function(self) 65 | return tostring(self:get_name()) 66 | end, 67 | --- Return this StringName's amount of characters. 68 | -- @function __len 69 | -- @treturn int 70 | __len = function(self) 71 | return #self:get_name() 72 | end, 73 | __index = methods, 74 | --- Concatenates values. 75 | -- @function __concat 76 | -- @param a First value, stringified with `GD.str` 77 | -- @param b First value, stringified with `GD.str` 78 | -- @treturn String 79 | __concat = concat_gdvalues, 80 | }) 81 | -------------------------------------------------------------------------------- /src/godot_transform.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_transform.lua Wrapper for GDNative's Transform 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Transform metatype, wrapper for `godot_transform`. 25 | -- Constructed using the idiom `Transform(...)`, which calls `__new`. 26 | -- typedef union godot_transform { 27 | -- uint8_t data[48]; 28 | -- float elements[12]; 29 | -- struct { Basis basis; Vector3 origin; }; 30 | -- } godot_transform; 31 | -- @classmod Transform 32 | 33 | local methods = { 34 | fillvariant = api.godot_variant_new_transform, 35 | varianttype = VariantType.Transform, 36 | 37 | --- Returns the inverse of the transform, under the assumption that the transformation is composed of rotation and translation (no scaling, use `affine_inverse` for transforms with scaling). 38 | -- @function inverse 39 | -- @treturn Transform 40 | inverse = api.godot_transform_inverse, 41 | --- Returns the inverse of the transform, under the assumption that the transformation is composed of rotation, scaling and translation. 42 | -- @function affine_inverse 43 | -- @treturn Transform 44 | affine_inverse = api.godot_transform_affine_inverse, 45 | --- Returns the transform with the basis orthogonal (90 degrees), and normalized axis vectors. 46 | -- @function orthonormalized 47 | -- @treturn Transform 48 | orthonormalized = api.godot_transform_orthonormalized, 49 | --- Rotates the transform around the given `axis` by the given `angle` (in radians), using matrix multiplication. 50 | -- The axis must be a normalized vector. 51 | -- @function rotated 52 | -- @tparam Vector3 axis 53 | -- @tparam number angle 54 | -- @treturn Transform 55 | rotated = api.godot_transform_rotated, 56 | --- Scales basis and origin of the transform by the given `scale` factor, using matrix multiplication. 57 | -- @function scaled 58 | -- @tparam Vector3 scale 59 | -- @treturn Transform 60 | scaled = api.godot_transform_scaled, 61 | --- Translates the transform by the given `offset`, relative to the transform's basis vectors. 62 | -- Unlike rotated and scaled, this does not use matrix multiplication. 63 | -- @function treturn 64 | -- @tparam Vector3 offset 65 | -- @treturn Transform 66 | translated = api.godot_transform_translated, 67 | --- Returns a copy of the transform rotated such that its -Z axis points towards the `target` position. 68 | -- The transform will first be rotated around the given up vector, and then fully aligned to the `target` by a further rotation around an axis perpendicular to both the `target` and `up` vectors. 69 | -- Operations take place in global space. 70 | -- @function looking_at 71 | -- @tparam Vector3 target 72 | -- @tparam Vector3 up 73 | -- @treturn Transform 74 | looking_at = api.godot_transform_looking_at, 75 | --- Transforms the given Plane 76 | -- @function xform_plane 77 | -- @tparam Plane plane 78 | -- @treturn Plane 79 | -- @see xform 80 | xform_plane = api.godot_transform_xform_plane, 81 | --- Inverse-transforms the given Plane 82 | -- @function xform_inv_plane 83 | -- @tparam Plane plane 84 | -- @treturn Plane 85 | -- @see xform 86 | xform_inv_plane = api.godot_transform_xform_inv_plane, 87 | --- Transforms the given Vector3 88 | -- @function xform_vector3 89 | -- @tparam Vector3 vector 90 | -- @treturn Vector3 91 | -- @see xform 92 | xform_vector3 = api.godot_transform_xform_vector3, 93 | --- Inverse-transforms the given Vector3 94 | -- @function xform_inv_vector3 95 | -- @tparam Vector3 vector 96 | -- @treturn Vector3 97 | -- @see xform 98 | xform_inv_vector3 = api.godot_transform_xform_inv_vector3, 99 | --- Transforms the given AABB 100 | -- @function xform_aabb 101 | -- @tparam AABB aabb 102 | -- @treturn AABB 103 | -- @see xform 104 | xform_aabb = api.godot_transform_xform_aabb, 105 | --- Inverse-transforms the given AABB 106 | -- @function xform_inv_aabb 107 | -- @tparam AABB aabb 108 | -- @treturn AABB 109 | -- @see xform 110 | xform_inv_aabb = api.godot_transform_xform_inv_aabb, 111 | --- Transforms the given `Vector3`, `Plane`, `AABB`, or `PoolVector3Array` by this transform. 112 | -- @function xform 113 | -- @tparam Vector3|Plane|AABB|PoolVector3Array value 114 | -- @treturn Vector3|Plane|AABB|PoolVector3Array Transformed value 115 | xform = function(self, value) 116 | if ffi_istype(Vector3, value) then 117 | return self:xform_vector3(value) 118 | elseif ffi_istype(Plane, value) then 119 | return self:xform_plane(value) 120 | elseif ffi_istype(AABB, value) then 121 | return self:xform_aabb(value) 122 | elseif ffi_istype(PoolVector3Array, value) then 123 | local array = PoolVector3Array() 124 | array:resize(#value) 125 | for i, v in ipairs(value) do 126 | array:set(i, self:xform_vector3(v)) 127 | end 128 | return array 129 | end 130 | end, 131 | --- Inverse-transforms the given `Vector3`, `Plane`, `AABB`, or `PoolVector3Array` by this transform. 132 | -- @function xform_inv 133 | -- @tparam Vector3|Plane|AABB|PoolVector3Array value 134 | -- @treturn Vector3|Plane|AABB|PoolVector3Array Transformed value 135 | xform_inv = function(self, value) 136 | if ffi_istype(Vector3, value) then 137 | return self:xform_inv_vector3(value) 138 | elseif ffi_istype(Plane, value) then 139 | return self:xform_inv_plane(value) 140 | elseif ffi_istype(AABB, value) then 141 | return self:xform_inv_aabb(value) 142 | elseif ffi_istype(PoolVector3Array, value) then 143 | local array = PoolVector3Array() 144 | array:resize(#value) 145 | for i, v in ipairs(value) do 146 | array:set(i, self:xform_inv_vector3(v)) 147 | end 148 | return array 149 | end 150 | end, 151 | } 152 | 153 | --- Constants 154 | -- @section constants 155 | 156 | --- Transform with no translation, rotation or scaling applied. 157 | -- @field IDENTITY Transform(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) 158 | 159 | --- Transform with mirroring applied perpendicular to the YZ plane. 160 | -- @field FLIP_X Transform(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) 161 | 162 | --- Transform with mirroring applied perpendicular to the XZ plane. 163 | -- @field FLIP_Y Transform(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0) 164 | 165 | --- Transform with mirroring applied perpendicular to the XY plane. 166 | -- @field FLIP_Z Transform(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0) 167 | 168 | --- @section end 169 | 170 | methods.IDENTITY = ffi_new('godot_transform', { elements = { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 } }) 171 | methods.FLIP_X = ffi_new('godot_transform', { elements = { -1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 } }) 172 | methods.FLIP_Y = ffi_new('godot_transform', { elements = { 1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0 } }) 173 | methods.FLIP_Z = ffi_new('godot_transform', { elements = { 1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0 } }) 174 | 175 | --- Metamethods 176 | -- @section metamethods 177 | Transform = ffi_metatype('godot_transform', { 178 | --- Transform constructor, called by the idiom `Transform(...)`. 179 | -- 180 | -- * `Transform()`: `IDENTITY` transform 181 | -- * `Transform(Basis basis[, Vector3 origin = Vector3.ZERO])`: set `basis` and `origin` 182 | -- * `Transform(Quat quat[, Vector3 origin = Vector3.ZERO])`: set `basis` from quaternion and `origin` 183 | -- * `Transform(Vector3 euler[, Vector3 origin = Vector3.ZERO])`: set `basis` from Euler angles and `origin` 184 | -- * `Transform(Vector3 x, Vector3 y, Vector3 z, Vector3 origin)`: constructs the transform from the four column vectors. 185 | -- * `Transform(Transform other)`: returns a copy of `other` 186 | -- @function __new 187 | -- @param ... 188 | -- @treturn Transform 189 | __new = function(mt, x, y, z, origin) 190 | if ffi_istype(mt, x) then 191 | return ffi_new(mt, x) 192 | end 193 | if not x then 194 | return ffi_new(mt, mt.IDENTITY) 195 | elseif ffi_istype(Vector3, x) then 196 | local self = ffi_new(mt) 197 | api.godot_transform_new_with_axis_origin(self, x, y, z, origin) 198 | return self 199 | else 200 | return ffi_new(mt, { basis = Basis(x), origin = y }) 201 | end 202 | end, 203 | __index = methods, 204 | --- Returns a Lua string representation of this transform. 205 | -- @function __tostring 206 | -- @treturn string 207 | __tostring = gd_tostring, 208 | --- Concatenates values. 209 | -- @function __concat 210 | -- @param a First value, stringified with `GD.str` 211 | -- @param b First value, stringified with `GD.str` 212 | -- @treturn String 213 | __concat = concat_gdvalues, 214 | --- Multiplication operation. 215 | -- Either multiply another Transform or `xform` value. 216 | -- @tparam Transform self 217 | -- @tparam Transform|Vector3|Plane|AABB|PoolVector3Array b 218 | -- @treturn Transform|Vector3|Plane|AABB|PoolVector3Array Transformed value 219 | __mul = function(self, b) 220 | if ffi_istype(Transform, b) then 221 | return api.godot_transform_operator_multiply(self, b) 222 | else 223 | return self:xform(b) 224 | end 225 | end, 226 | --- Equality operation 227 | -- If either `a` or `b` are not of type `Transform`, always return `false`. 228 | -- @function __eq 229 | -- @tparam Transform a 230 | -- @tparam Transform b 231 | -- @treturn bool 232 | __eq = function(a, b) 233 | return ffi_istype(Transform, a) and ffi_istype(Transform, b) and a.basis == b.basis and a.origin == b.origin 234 | end, 235 | }) 236 | -------------------------------------------------------------------------------- /src/godot_transform2d.lua: -------------------------------------------------------------------------------- 1 | -- @file godot_transform2d.lua Wrapper for GDNative's Transform2D 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Transform2D metatype, wrapper for `godot_transform2d`. 25 | -- Constructed using the idiom `Transform2D(...)`, which calls `__new`. 26 | -- typedef union godot_transform2d { 27 | -- uint8_t data[24]; 28 | -- float elements[6]; 29 | -- Vector2 columns[3]; 30 | -- struct { Vector2 x, y, origin; }; 31 | -- } godot_transform2d; 32 | -- @classmod Transform2D 33 | 34 | local methods = { 35 | fillvariant = api.godot_variant_new_transform2d, 36 | varianttype = VariantType.Transform2D, 37 | 38 | --- Returns the inverse of the transform, under the assumption that the transformation is composed of rotation and translation (no scaling, use `affine_inverse` for transforms with scaling). 39 | -- @function inverse 40 | -- @treturn Transform2D 41 | -- @see affine_inverse 42 | inverse = api.godot_transform2d_inverse, 43 | --- Returns the inverse of the transform, under the assumption that the transformation is composed of rotation, scaling and translation. 44 | -- @function affine_inverse 45 | -- @treturn Transform2D 46 | affine_inverse = api.godot_transform2d_affine_inverse, 47 | --- Returns the transform's rotation (in radians). 48 | -- @function get_rotation 49 | -- @treturn number 50 | get_rotation = api.godot_transform2d_get_rotation, 51 | --- Returns the transform's origin (translation). 52 | -- @function get_origin 53 | -- @treturn Vector2 54 | get_origin = api.godot_transform2d_get_origin, 55 | --- Returns the scale. 56 | -- @function get_scale 57 | -- @treturn Vector2 58 | get_scale = api.godot_transform2d_get_scale, 59 | --- Returns the transform with the basis orthogonal (90 degrees), and normalized axis vectors (scale of 1 or -1). 60 | -- @function orthonormalized 61 | -- @treturn Transform2D 62 | orthonormalized = api.godot_transform2d_orthonormalized, 63 | --- Rotates the transform by the given `angle` (in radians), using matrix multiplication. 64 | -- @function rotated 65 | -- @tparam number angle 66 | -- @treturn Transform2D 67 | rotated = api.godot_transform2d_rotated, 68 | --- Scales the transform by the given `scale` factor, using matrix multiplication. 69 | -- @function scaled 70 | -- @tparam Vector2 scale 71 | -- @treturn Transform2D 72 | scaled = api.godot_transform2d_scaled, 73 | --- Translates the transform by the given `offset`, relative to the transform's basis vectors. 74 | -- Unlike rotated and scaled, this does not use matrix multiplication. 75 | -- @function translated 76 | -- @tparam Vector2 offset 77 | -- @treturn Transform2D 78 | translated = api.godot_transform2d_translated, 79 | --- Transforms the given Vector2. 80 | -- @function xform_vector2 81 | -- @tparam Vector2 vector 82 | -- @treturn Vector2 83 | -- @see xform 84 | xform_vector2 = api.godot_transform2d_xform_vector2, 85 | --- Inverse-transforms the given Vector2. 86 | -- @function xform_inv_vector2 87 | -- @tparam Vector2 vector 88 | -- @treturn Vector2 89 | -- @see xform_inv 90 | xform_inv_vector2 = api.godot_transform2d_xform_inv_vector2, 91 | --- Returns a vector transformed (multiplied) by the basis matrix. 92 | -- This method does not account for translation (the origin vector). 93 | -- @function basis_xform_vector2 94 | -- @tparam Vector2 vector 95 | -- @treturn Vector2 96 | basis_xform = api.godot_transform2d_basis_xform_vector2, 97 | --- Returns a vector transformed (multiplied) by the inverse basis matrix. 98 | -- This method does not account for translation (the origin vector). 99 | -- @function basis_xform_inv 100 | -- @tparam Vector2 vector 101 | -- @treturn Vector2 102 | basis_xform_inv = api.godot_transform2d_basis_xform_inv_vector2, 103 | --- Returns a transform interpolated between this transform and another by a given `weight` (on the range of 0.0 to 1.0). 104 | -- @function interpolate_with 105 | -- @tparam Transform2D transform 106 | -- @tparam number weight 107 | interpolate_with = api.godot_transform2d_interpolate_with, 108 | --- Transforms the given Rect2. 109 | -- @function xform_rect2 110 | -- @tparam Rect2 vector 111 | -- @treturn Rect2 112 | -- @see xform 113 | xform_rect2 = api.godot_transform2d_xform_rect2, 114 | --- Inverse-transforms the given Rect2. 115 | -- @function xform_inv_rect2 116 | -- @tparam Rect2 vector 117 | -- @treturn Rect2 118 | -- @see xform_inv 119 | xform_inv_rect2 = api.godot_transform2d_xform_inv_rect2, 120 | --- Transforms the given `Vector2`, `Rect2`, or `PoolVector2Array` by this transform. 121 | -- @function xform 122 | -- @tparam Vector2|Rect2|PoolVector2Array value 123 | -- @treturn Vector2|Rect2|PoolVector2Array Transformed value 124 | xform = function(self, value) 125 | if ffi_istype(Vector2, value) then 126 | return self:xform_vector2(value) 127 | elseif ffi_istype(Rect2, value) then 128 | return self:xform_rect2(value) 129 | elseif ffi_istype(PoolVector2Array, value) then 130 | local array = PoolVector2Array() 131 | array:resize(#value) 132 | for i, v in ipairs(value) do 133 | array:set(i, self:xform_vector2(v)) 134 | end 135 | return array 136 | end 137 | end, 138 | --- Inverse-transforms the given `Vector2`, `Rect2`, or `PoolVector2Array` by this transform. 139 | -- @function xform_inv 140 | -- @tparam Vector2|Rect2|PoolVector2Array value 141 | -- @treturn Vector2|Rect2|PoolVector2Array Transformed value 142 | xform_inv = function(self, value) 143 | if ffi_istype(Vector2, value) then 144 | return self:xform_inv_vector2(value) 145 | elseif ffi_istype(Rect2, value) then 146 | return self:xform_inv_rect2(value) 147 | elseif ffi_istype(PoolVector2Array, value) then 148 | local array = PoolVector2Array() 149 | array:resize(#value) 150 | for i, v in ipairs(value) do 151 | array:set(i, self:xform_inv_vector2(v)) 152 | end 153 | return array 154 | end 155 | end, 156 | } 157 | 158 | --- Constants 159 | -- @section constants 160 | 161 | --- The identity Transform2D with no translation, rotation or scaling applied. 162 | -- @field IDENTITY Transform2D(1, 0, 0, 1, 0, 0) 163 | 164 | --- The Transform2D that will flip something along the X axis. 165 | -- @field FLIP_X Transform2D(-1, 0, 0, 1, 0, 0) 166 | 167 | --- The Transform2D that will flip something along the Y axis. 168 | -- @field FLIP_Y Transform2D(1, 0, 0, -1, 0, 0) 169 | 170 | --- @section end 171 | 172 | methods.IDENTITY = ffi_new('godot_transform2d', { elements = { 1, 0, 0, 1, 0, 0 } }) 173 | methods.FLIP_X = ffi_new('godot_transform2d', { elements = { -1, 0, 0, 1, 0, 0 } }) 174 | methods.FLIP_Y = ffi_new('godot_transform2d', { elements = { 1, 0, 0, -1, 0, 0 } }) 175 | 176 | --- Metamethods 177 | -- @section metamethods 178 | Transform2D = ffi_metatype('godot_transform2d', { 179 | --- Transform2D constructor, called by the idiom `Transform2D(...)`. 180 | -- 181 | -- * `Transform2D()`: creates an `IDENTITY` transform. 182 | -- * `Transform2D(number angle, Vector2 position)`: constructs the transform from a given angle (in radians) and position. 183 | -- * `Transform2D(Vector2 x, Vector2 y, Vector2 origin)`: constructs the transform from the three column vectors. 184 | -- * `Transform2D(Transform2D other)`: returns a copy of `other` 185 | -- @function __new 186 | -- @param ... 187 | -- @treturn Transform2D 188 | __new = function(mt, x, y, origin) 189 | if ffi_istype(mt, x) then 190 | return ffi_new(mt, x) 191 | end 192 | local self = ffi_new(mt) 193 | if not x then 194 | api.godot_transform2d_new_identity(self) 195 | elseif tonumber(x) then 196 | api.godot_transform2d_new(self, x, y) 197 | else 198 | api.godot_transform2d_new_axis_origin(self, x, y, origin) 199 | end 200 | return self 201 | end, 202 | __index = methods, 203 | --- Returns a Lua string representation of this transform. 204 | -- @function __tostring 205 | -- @treturn string 206 | __tostring = gd_tostring, 207 | --- Concatenates values. 208 | -- @function __concat 209 | -- @param a First value, stringified with `GD.str` 210 | -- @param b First value, stringified with `GD.str` 211 | -- @treturn String 212 | __concat = concat_gdvalues, 213 | --- Multiplication operation. 214 | -- Either multiply another Transform2D or `xform` value. 215 | -- @tparam Transform2D self 216 | -- @tparam Transform2D|Vector2|Rect2|PoolVector2Array b 217 | -- @treturn Transform2D|Vector2|Rect2|PoolVector2Array Transformed value 218 | __mul = function(self, b) 219 | if ffi_istype(Transform2D, b) then 220 | return api.godot_transform2d_operator_multiply(self, b) 221 | else 222 | return self:xform(b) 223 | end 224 | end, 225 | --- Equality operation 226 | -- If either `a` or `b` are not of type `Transform2D`, always return `false`. 227 | -- @function __eq 228 | -- @tparam Transform2D a 229 | -- @tparam Transform2D b 230 | -- @treturn bool 231 | __eq = function(a, b) 232 | return ffi_istype(Transform2D, a) and ffi_istype(Transform2D, b) and a.elements[0] == b.elements[0] and a.elements[1] == b.elements[1] and a.elements[2] == b.elements[2] 233 | end, 234 | }) 235 | 236 | 237 | -------------------------------------------------------------------------------- /src/late_globals.lua: -------------------------------------------------------------------------------- 1 | -- @file late_globals.lua GD table and _G metatable 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Godot enumerations and functions, available globally as the `GD` table. 25 | -- Godot global constants from `godot_get_global_constants` are also available, 26 | -- like `OK`, `ERR_ALREADY_EXISTS`, `TYPE_NIL`, `KEY_A`, `BUTTON_LEFT`, 27 | -- `JOY_START`, `HALIGN_CENTER` and `MIDI_MESSAGE_NOTE_OFF`. 28 | -- @module GD 29 | 30 | GD = { 31 | --- (`const godot_gdnative_core_api_struct *`) GDNative core API 1.0 32 | api = api, 33 | --- (`const godot_gdnative_core_1_1_api_struct *`) GDNative core API 1.1 34 | api_1_1 = api_1_1, 35 | --- (`const godot_gdnative_core_1_2_api_struct *`) GDNative core API 1.2 36 | api_1_2 = api_1_2, 37 | --- (`Object`) [GDNativeLibrary](https://docs.godotengine.org/en/stable/classes/class_gdnativelibrary.html) instance 38 | gdnativelibrary = gdnativelibrary, 39 | --- `Enumerations.Error` 40 | Error = Error, 41 | --- `Enumerations.VariantType` 42 | VariantType = VariantType, 43 | --- `Enumerations.CallError` 44 | CallError = CallError, 45 | --- `Enumerations.RPCMode` 46 | RPCMode = RPCMode, 47 | --- `Enumerations.PropertyHint` 48 | PropertyHint = PropertyHint, 49 | --- `Enumerations.PropertyUsage` 50 | PropertyUsage = PropertyUsage, 51 | --- Project version: 0.5.2 52 | _VERSION = '0.5.2', 53 | } 54 | 55 | local global_constants = api.godot_get_global_constants() 56 | for k, v in pairs(global_constants) do 57 | GD[k:to_ascii()] = v 58 | end 59 | api.godot_dictionary_destroy(global_constants) 60 | 61 | --- Convert any value to a `godot_string`. 62 | -- If `value` is already a `godot_string`, return it unmodified. 63 | -- Otherwise, constructs a Variant and calls `as_string` on it. 64 | -- @function GD.str 65 | -- @param value Value to be stringified 66 | -- @treturn String 67 | GD.str = str 68 | 69 | --- Returns the Lua script instance associated with an `Object`, if it 70 | -- has a Lua Script attached. 71 | -- @function GD.get_lua_instance 72 | -- @tparam Object object 73 | -- @treturn[1] LuaScriptInstance 74 | -- @treturn[2] nil If Object has no Lua Script attached 75 | GD.get_lua_instance = get_lua_instance 76 | 77 | --- Print a message to Godot's Output panel, with values separated by tabs 78 | function GD.print(...) 79 | local message = String(string_join('\t', ...)) 80 | api.godot_print(message) 81 | end 82 | _G.print = GD.print 83 | 84 | --- Print a warning to Godot's Output panel, with values separated by tabs 85 | function GD.print_warning(...) 86 | local info = debug_getinfo(2, 'nSl') 87 | local message = string_join('\t', ...) 88 | api.godot_print_warning(message, info.name, info.short_src, info.currentline) 89 | end 90 | 91 | --- Print an error to Godot's Output panel, with values separated by tabs 92 | function GD.print_error(...) 93 | local info = debug_getinfo(2, 'nSl') 94 | local message = string_join('\t', ...) 95 | api.godot_print_error(message, info.name, info.short_src, info.currentline) 96 | end 97 | 98 | local ResourceLoader = api.godot_global_get_singleton("ResourceLoader") 99 | --- Loads a Resource by path, similar to GDScript's [load](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-method-load) 100 | function GD.load(path) 101 | return ResourceLoader:load(path) 102 | end 103 | 104 | 105 | --- Yield the current running Lua thread, returning a wrapper Object with the `lps_coroutine.lua` script attached. 106 | -- If an `object` and `signal_name` are passed, this coroutine will resume automatically when object emits the signal. 107 | -- If the same coroutine yields more than once, the same object will be returned. 108 | -- This is similar to GDScript's [yield](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-method-yield). 109 | -- @usage 110 | -- -- self is a Node 111 | -- GD.yield(self:get_tree():create_timer(2), "timeout") 112 | -- print('2 seconds have passed!') 113 | -- @tparam[opt] Object object 114 | -- @tparam[opt] string|String|StringName signal_name 115 | -- @return Value passed to coroutine object's `resume` call, if any 116 | -- @see lps_coroutine.lua 117 | function GD.yield(object, signal_name) 118 | local co, is_main = coroutine_running() 119 | assert(co and not is_main, "GD.yield can be called only from script methods") 120 | local co_obj = get_script_instance_for_lua_object(co) 121 | if object and signal_name then 122 | object:connect(signal_name, co_obj, "resume", Array(), Object.CONNECT_ONESHOT) 123 | end 124 | return coroutine_yield(co_obj) 125 | end 126 | 127 | local Engine = api.godot_global_get_singleton("Engine") 128 | setmetatable(_G, { 129 | __index = function(self, key) 130 | local gd_key = String(key) 131 | if Engine:has_singleton(gd_key) then 132 | local singleton = api.godot_global_get_singleton(key) 133 | rawset(self, key, singleton) 134 | return singleton 135 | end 136 | local cls = ClassWrapper_cache[key] 137 | if cls then 138 | rawset(self, key, cls) 139 | return cls 140 | end 141 | end, 142 | }) 143 | 144 | -- References are already got, just register them globally 145 | _G.Engine = Engine 146 | _G.ClassDB = ClassDB 147 | _G.ResourceLoader = ResourceLoader 148 | -- These classes are registered with a prepending "_" in ClassDB 149 | File = ClassWrapper_cache._File 150 | Directory = ClassWrapper_cache._Directory 151 | Thread = ClassWrapper_cache._Thread 152 | Mutex = ClassWrapper_cache._Mutex 153 | Semaphore = ClassWrapper_cache._Semaphore 154 | -------------------------------------------------------------------------------- /src/lua_globals.lua: -------------------------------------------------------------------------------- 1 | -- @file lua_globals.lua Extra global Lua functions and basic types 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Extra global Lua functions and basic types 25 | -- @module Globals 26 | 27 | --- Return `value` converted to light userdata with the return of `lua_topointer` 28 | -- @function touserdata 29 | -- @param value Value to be converted 30 | -- @return Light userdata 31 | 32 | --- Create or reuse a Lua thread (coroutine) with the given function/callable object 33 | -- @warning Errorred threads cannot be reused 34 | -- @function setthreadfunc 35 | -- @tparam thread|nil thread Thread to be reused. Pass `nil` to create a new one 36 | -- @tparam function|table|userdata f Function or other callable 37 | -- @treturn thread Reused or created thread 38 | 39 | --- Alias for `GD.print` 40 | -- @function print 41 | -- @param ... 42 | 43 | --- Search for singleton objects with `Engine:has_singleton(key)` and classes with 44 | -- `ClassDB:class_exists(key)`. 45 | -- Cache any values found, to avoid future calls. 46 | -- Called when indexing the global table `_G` with a currently unknown key. 47 | -- @function _G:__index 48 | -- @param key 49 | -- @treturn[1] Object Singleton object, if `Engine:has_singleton(key)` 50 | -- @treturn[2] OOP.ClassWrapper Class wrapper, if `ClassDB:class_exists(key)` 51 | -- @treturn[3] nil 52 | 53 | 54 | --- Scalar types 55 | -- @section scalar_types 56 | 57 | --- Boolean type `godot_bool` 58 | bool = ffi_typeof('godot_bool') 59 | --- Integer type `godot_int` 60 | int = ffi_typeof('godot_int') 61 | --- Float type `godot_real` 62 | float = ffi_typeof('godot_real') 63 | 64 | -------------------------------------------------------------------------------- /src/lua_math_extras.lua: -------------------------------------------------------------------------------- 1 | -- @file lua_math_extras.lua Extra functionality for the `math` library 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Extra functionality for Lua's `math` library. 25 | -- @module math_extras 26 | 27 | --- Returns the value `x` clamped between an upper (`max`) and lower bounds (`min`). 28 | -- Any values comparable by order, that is, with a less than operator, can be passed in. 29 | -- @tparam Vector2|Vector3|Color|number x 30 | -- @tparam Vector2|Vector3|Color|number min 31 | -- @tparam Vector2|Vector3|Color|number max 32 | -- @treturn Vector2|Vector3|Color|number 33 | function math.clamp(x, min, max) 34 | if x < min then 35 | return min 36 | elseif x > max then 37 | return max 38 | else 39 | return x 40 | end 41 | end 42 | 43 | --- Linearly interpolates values `from` and `to` by `amount`. 44 | -- Equivalent to `from + (amount * (to - from))`. 45 | -- @tparam Vector2|Vector3|Color|Quat|number from 46 | -- @tparam Vector2|Vector3|Color|Quat|number to 47 | -- @tparam number amount 48 | -- @treturn Vector2|Vector3|Color|Quat|number 49 | function math.lerp(from, to, amount) 50 | return from + (amount * (to - from)) 51 | end 52 | -------------------------------------------------------------------------------- /src/lua_object_struct.lua: -------------------------------------------------------------------------------- 1 | -- @file lua_object_struct.lua Helper struct for storing Lua references 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Internal struct to store references to Lua objects, like tables and coroutines. 25 | -- 26 | -- References are stored in a global cache table, indexed by their own address, 27 | -- and are only unreferenced when `LuaObject_destroy` is called. 28 | -- Thus this struct must used with care. 29 | -- 30 | -- This is used internally for having `LuaScript` and `LuaScriptInstance` 31 | -- be structs that reference tables. 32 | -- 33 | -- typedef struct { 34 | -- void *__address; 35 | -- } lps_lua_object; 36 | -- 37 | -- @module LuaObject 38 | 39 | ffi_cdef[[ 40 | typedef struct { 41 | void *__address; 42 | } lps_lua_object; 43 | ]] 44 | 45 | local lps_lua_objects = {} 46 | 47 | --- Helper function to convert a `void *` to a table index. 48 | local function pointer_to_index(ptr) 49 | return tonumber(ffi_cast('uintptr_t', ptr)) 50 | end 51 | 52 | --- Gets the Lua object referenced by a LuaObject 53 | local function LuaObject_get(self) 54 | return lps_lua_objects[pointer_to_index(self.__address)] 55 | end 56 | 57 | --- Sets the Lua object referenced by a LuaObject 58 | -- @tparam LuaObject self 59 | -- @param value 60 | local function LuaObject_set(self, value) 61 | lps_lua_objects[pointer_to_index(self.__address)] = value 62 | end 63 | 64 | --- Unreference the Lua object, removing it from cache. 65 | -- @see lps_lua_objects 66 | local function LuaObject_destroy(self) 67 | LuaObject_set(self, nil) 68 | end 69 | 70 | --- @type LuaObject 71 | local LuaObject = ffi_metatype('lps_lua_object', { 72 | --- Wrapped reference memory address 73 | -- @tfield void* __address 74 | 75 | --- LuaObject constructor, called by the idiom `LuaObject(obj)`. 76 | -- 77 | -- This registers the object in the global cache table, indexed by its 78 | -- own address. 79 | -- @function __new 80 | -- @param obj Lua object 81 | -- @local 82 | __new = function(mt, obj) 83 | local self = ffi_new(mt, touserdata(obj)) 84 | LuaObject_set(self, obj) 85 | return self 86 | end, 87 | --- Forwards indexing to referenced Lua object 88 | -- @function __index 89 | -- @param index 90 | -- @return value 91 | __index = function(self, index) 92 | return LuaObject_get(self)[index] 93 | end, 94 | --- Forwards indexing to referenced Lua object 95 | -- @function __newindex 96 | -- @param index 97 | -- @param value 98 | __newindex = function(self, index, value) 99 | LuaObject_get(self)[index] = value 100 | end, 101 | --- Forwards length operator to referenced Lua object 102 | -- @function __len 103 | -- @return Object length, if supported 104 | __len = function(self) 105 | return #LuaObject_get(self) 106 | end, 107 | --- Forwards `pairs` to referenced Lua object 108 | -- @function __pairs 109 | __pairs = function(self) 110 | return pairs(LuaObject_get(self)) 111 | end, 112 | --- Forwards `ipairs` to referenced Lua object 113 | -- @function __ipairs 114 | __ipairs = function(self) 115 | return ipairs(LuaObject_get(self)) 116 | end, 117 | --- Forwards call to referenced Lua object 118 | -- @function __call 119 | -- @param ... 120 | __call = function(self, ...) 121 | return LuaObject_get(self)(...) 122 | end, 123 | }) 124 | -------------------------------------------------------------------------------- /src/lua_object_wrapper.lua: -------------------------------------------------------------------------------- 1 | -- @file lua_object_wrapper.lua Script instances for lua objects 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Helper functions for wrapping a Lua Object in a Godot Object. 25 | -- Currently only coroutines are supported. 26 | -- @module lua_object_wrapper 27 | -- @local 28 | 29 | local library_resource_dir = gdnativelibrary.resource_path:get_base_dir() 30 | 31 | --- Global cache of Lua object wrappers 32 | local lps_lua_object_references = setmetatable({}, weak_kv) 33 | 34 | --- Get or create a Godot Object that wraps `lua_obj`. 35 | -- Currently only coroutines are supported. 36 | local function get_script_instance_for_lua_object(lua_obj) 37 | local ref = lps_lua_object_references[lua_obj] 38 | if ref ~= nil then 39 | return ref 40 | end 41 | 42 | local t = type(lua_obj) 43 | if t == 'thread' then 44 | lps_next_instance_data = lua_obj 45 | local godot_obj = GD.load(library_resource_dir:plus_file('lps_coroutine.lua')):new() 46 | lps_lua_object_references[lua_obj] = godot_obj 47 | return godot_obj 48 | else 49 | error(string_format('Lua object of type %q is not supported yet', t)) 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /src/lua_package_extras.lua: -------------------------------------------------------------------------------- 1 | -- @file lua_package_extras.lua Extra functionality for the `package` library 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Extra functionality for Lua's `package` library. 25 | -- Patch the `package.loaders`/`package.searchers` to use `searchpath`, 26 | -- so that `require` can find files relative to the `res://` and 27 | -- executable folder. 28 | -- @module package_extras 29 | 30 | local ProjectSettings, OS = ProjectSettings, OS 31 | local execdir_repl = 32 | OS:has_feature("standalone") 33 | and OS:get_executable_path():get_base_dir() 34 | or ProjectSettings:globalize_path("res://") 35 | execdir_repl = tostring(execdir_repl:trim_suffix('/')) 36 | 37 | -- Supports "res://" and "user://" paths 38 | -- Replaces "!" for executable path on standalone builds or project path otherwise 39 | local function searchpath(name, path, sep, rep) 40 | sep = sep or '.' 41 | rep = rep or '/' 42 | if sep ~= '' then 43 | name = name:replace(sep, rep) 44 | end 45 | local notfound = {} 46 | local f = File:new() 47 | for template in path:gmatch('[^;]+') do 48 | local filename = template:replace('?', name):replace('!', execdir_repl) 49 | if f:open(filename, File.READ) == Error.OK then 50 | return filename, f 51 | else 52 | table_insert(notfound, string_format("\n\tno file %q", filename)) 53 | end 54 | end 55 | return nil, table_concat(notfound) 56 | end 57 | 58 | local function lua_searcher(name) 59 | local filename, open_file_or_err = searchpath(name, package.path) 60 | if not filename then 61 | return open_file_or_err 62 | end 63 | local file_len = open_file_or_err:get_len() 64 | local contents = open_file_or_err:get_buffer(file_len):get_string() 65 | open_file_or_err:close() 66 | return assert(loadstring(contents, filename)) 67 | end 68 | 69 | local function c_searcher(name, name_override) 70 | local filename, open_file_or_err = searchpath(name, package.cpath) 71 | if not filename then 72 | return open_file_or_err 73 | end 74 | filename = tostring(open_file_or_err:get_path_absolute()) 75 | open_file_or_err:close() 76 | local func_suffix = (name_override or name):replace('.', '_') 77 | -- Split module name if a "-" is found 78 | local igmark = string_find(func_suffix, '-', 1, false) 79 | if igmark then 80 | local funcname = 'luaopen_' .. func_suffix:sub(1, igmark - 1) 81 | local f = package_loadlib(filename, funcname) 82 | if f then return f end 83 | func_suffix = func_suffix:sub(igmark + 1) 84 | end 85 | local f, err = package_loadlib(filename, 'luaopen_' .. func_suffix) 86 | return assert(f, string_format('error loading module %q from file %q:\n\t%s', name_override or name, filename, err)) 87 | end 88 | 89 | local function c_root_searcher(name) 90 | local root_name = name:match('^([^.]+)%.') 91 | if not root_name then 92 | return nil 93 | end 94 | return c_searcher(root_name, name) 95 | end 96 | 97 | --- Searches for the given `name` in the given path. 98 | -- Similar to Lua 5.2+ [package.searchpath](https://www.lua.org/manual/5.2/manual.html#pdf-package.searchpath), 99 | -- but using Godot Files instead, so that paths like `res://` and `user://` are 100 | -- supported. 101 | -- 102 | -- `!` characters in `path` templates are replaced by the directory of the game/app 103 | -- executable when running a standalone build (when `OS:has_feature("standalone")`) 104 | -- or by the project's resource path otherwise (`ProjectSettings:globalize_path("res://")`). 105 | -- @function package.searchpath 106 | -- @param name 107 | -- @param path 108 | -- @param[opt="."] separator 109 | -- @param[opt="/"] replacement 110 | -- @treturn[1] string Found file name 111 | -- @treturn[2] nil 112 | -- @treturn[2] string Error message 113 | function package.searchpath(...) 114 | local filename, open_file_or_err = searchpath(...) 115 | if not filename then 116 | return nil, open_file_or_err 117 | else 118 | open_file_or_err:close() 119 | return filename 120 | end 121 | end 122 | 123 | local searchers = package.searchers or package.loaders 124 | searchers[2] = lua_searcher 125 | searchers[3] = c_searcher 126 | searchers[4] = c_root_searcher 127 | 128 | local LUA_PATH_BEHAVIOR_SETTING = 'lua_pluginscript/package_path/behavior' 129 | local LUA_PATH_SETTING = 'lua_pluginscript/package_path/templates' 130 | local LUA_CPATH_BEHAVIOR_SETTING = 'lua_pluginscript/package_c_path/behavior' 131 | local LUA_CPATH_SETTING = 'lua_pluginscript/package_c_path/templates' 132 | 133 | local function add_project_setting(name, initial_value) 134 | if not ProjectSettings:has_setting(name) then 135 | ProjectSettings:set_setting(name, initial_value) 136 | end 137 | ProjectSettings:set_initial_value(name, initial_value) 138 | end 139 | 140 | local function add_project_setting_enum(name, enum_values) 141 | add_project_setting(name, 0) 142 | ProjectSettings:add_property_info { 143 | name = name, 144 | type = VariantType.Int, 145 | hint = PropertyHint.ENUM, 146 | hint_string = enum_values, 147 | } 148 | end 149 | 150 | add_project_setting_enum(LUA_PATH_BEHAVIOR_SETTING, 'Replace,Append,Prepend') 151 | add_project_setting(LUA_PATH_SETTING, PoolStringArray('res://?.lua', 'res://?/init.lua', 'res://addons/godot-lua-pluginscript/build/?.lua')) 152 | add_project_setting_enum(LUA_CPATH_BEHAVIOR_SETTING, 'Replace,Append,Prepend') 153 | add_project_setting(LUA_CPATH_SETTING, PoolStringArray('!/?.so', '!/loadall.so')) 154 | add_project_setting(LUA_CPATH_SETTING .. '.Windows', PoolStringArray('!/?.dll', '!/loadall.dll')) 155 | 156 | local lua_path = ProjectSettings:get_setting(LUA_PATH_SETTING) 157 | local lua_cpath = ProjectSettings:get_setting(LUA_CPATH_SETTING) 158 | local lua_path_behaviour = ProjectSettings:get_setting(LUA_PATH_BEHAVIOR_SETTING) 159 | local lua_cpath_behaviour = ProjectSettings:get_setting(LUA_CPATH_BEHAVIOR_SETTING) 160 | 161 | if lua_path_behaviour == 1 then 162 | lua_path:insert(0, package.path) 163 | elseif lua_path_behaviour == 2 then 164 | lua_path:append(package.path) 165 | end 166 | 167 | if lua_cpath_behaviour == 1 then 168 | lua_cpath:insert(0, package.cpath) 169 | elseif lua_cpath_behaviour == 2 then 170 | lua_cpath:append(package.cpath) 171 | end 172 | 173 | --- When Lua PluginScript is loaded, `package.path` is either replaced, 174 | -- appended or prepended by the paths in `lua_pluginscript/package_path/templates` project 175 | -- setting. 176 | -- 177 | -- The chosen behavior depends on the `lua_pluginscript/package_path/behavior` 178 | -- project setting. 179 | -- 180 | -- Default paths are `res://?.lua`, `res://?/init.lua` and 'res://addons/godot-lua-pluginscript/build/?.lua'. 181 | -- 182 | -- @see searchpath 183 | package.path = lua_path:join(';') 184 | 185 | --- When Lua PluginScript is loaded, `package.cpath` is either replaced, 186 | -- appended or prepended by the paths in `lua_pluginscript/package_c_path/templates` project 187 | -- setting. 188 | -- 189 | -- The chosen behavior depends on the `lua_pluginscript/package_c_path/behavior` 190 | -- project setting. 191 | -- 192 | -- Default paths are `!/?.dll` and `!/loadall.dll` on Windows, `!/?.so` and `!/loadall.so` elsewhere. 193 | -- 194 | -- @see searchpath 195 | package.cpath = lua_cpath:join(';') 196 | 197 | --- `dlopen`s the PluginScript library with `RTLD_LAZY | RTLD_GLOBAL`. 198 | -- This is necessary for loading Lua/C modules in POSIX systems. 199 | local function dlopen_self() 200 | ffi_cdef[[ 201 | void *dlopen(const char *filename, int flags); 202 | int dlclose(void *handle); 203 | ]] 204 | 205 | local RTLD_LAZY = 0x00001 206 | local RTLD_GLOBAL = 0x00100 207 | local currentLibraryPath = tostring(gdnativelibrary:get_current_library_path():replace("res://", execdir_repl .. '/')) 208 | -- Maintain a reference to dlopened library while PluginScript is loaded 209 | -- When the Lua state closes, GC will kick in and the library will get dlclosed 210 | pluginscript_callbacks.__dlopened_self = ffi_gc(ffi.C.dlopen(currentLibraryPath, RTLD_LAZY + RTLD_GLOBAL), ffi.C.dlclose) 211 | end 212 | 213 | if Array("Android", "iOS", "OSX", "Server", "X11"):has(OS:get_name()) then 214 | xpcall(dlopen_self, GD.print_error) 215 | end 216 | -------------------------------------------------------------------------------- /src/lua_string_extras.lua: -------------------------------------------------------------------------------- 1 | -- @file lua_string_extras.lua Extra functionality for the `string` library 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Extra functionality for Lua's `string` library. 25 | -- @module string_extras 26 | 27 | --- Returns a Lua string with all values joined by `separator`. 28 | -- `tostring` is called to each of the passed values before joining. 29 | -- @usage 30 | -- assert(string.join(',', 1, 2, 'three', Array()) == '1,2,three,[]') 31 | -- @function string.join 32 | -- @tparam string sep 33 | -- @param ... Values to be joined, stringified by `tostring`. 34 | -- @treturn string 35 | local function string_join(sep, ...) 36 | local result = {} 37 | for i = 1, select('#', ...) do 38 | local s = select(i, ...) 39 | table_insert(result, tostring(s)) 40 | end 41 | return table_concat(result, sep) 42 | end 43 | string.join = string_join 44 | 45 | --- Quote a value, alias for `string.format("%q", tostring(value))`. 46 | -- @function string.quote 47 | -- @param value 48 | -- @treturn string 49 | local function string_quote(value) 50 | return string_format('%q', tostring(value)) 51 | end 52 | string.quote = string_quote 53 | 54 | --- Performs plain substring substitution, with no characters in `pattern` or `replacement` being considered magic. 55 | -- @usage 56 | -- assert(string.replace('Dot.percent.arent.magic', '.', '%') == 'Dot%percent%arent%magic') 57 | -- @function string.replace 58 | -- @tparam string str 59 | -- @tparam string pattern 60 | -- @tparam string replacement 61 | -- @treturn string 62 | -- @see string.gsub 63 | 64 | -------------------------------------------------------------------------------- /src/pluginscript_instance.lua: -------------------------------------------------------------------------------- 1 | -- @file pluginscript_instance.lua Script instances struct and metatype 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Script instance metatable, the Lua side of a Lua script instance. 25 | -- These are created when a PluginScript is instanced and are only directly 26 | -- accessible in the script's functions as the `self` parameter or gotten from 27 | -- `Object`s using `GD.get_lua_instance`. 28 | -- 29 | -- typedef struct { 30 | -- godot_object *__owner; 31 | -- lps_lua_script *__script; 32 | -- lps_lua_object __data; 33 | -- } lps_script_instance; 34 | -- 35 | -- @module LuaScriptInstance 36 | 37 | ffi_cdef[[ 38 | typedef struct { 39 | godot_object *__owner; 40 | lps_lua_script *__script; 41 | lps_lua_object __data; 42 | } lps_script_instance; 43 | ]] 44 | 45 | --- Allocs and returns a pointer to a `LuaScriptInstance`. 46 | -- @tparam Object owner 47 | -- @tparam table script 48 | -- @param[opt={}] data 49 | local function LuaScriptInstance_new(owner, script, data) 50 | local self = ffi_cast('lps_script_instance *', api.godot_alloc(ffi_sizeof('lps_script_instance'))) 51 | self.__owner = owner 52 | self.__script = script 53 | self.__data = LuaObject(data or {}) 54 | return self 55 | end 56 | 57 | --- Frees all memory associated with a `LuaScriptInstance`. 58 | -- @tparam LuaScriptInstance self 59 | local function LuaScriptInstance_destroy(self) 60 | LuaObject_destroy(self.__data) 61 | api.godot_free(self) 62 | end 63 | 64 | local methods = { 65 | fillvariant = function(var, self) 66 | api.godot_variant_new_object(var, self.__owner) 67 | end, 68 | varianttype = VariantType.Object, 69 | 70 | --- Get a value from `__data`, bypassing the check for getters. 71 | -- @function rawget 72 | -- @param index 73 | -- @return 74 | rawget = function(self, index) 75 | return self.__data[index] 76 | end, 77 | --- Sets a value on the `__data`, bypassing the check for setters. 78 | -- @function rawset 79 | -- @param index 80 | -- @param value 81 | rawset = function(self, index, value) 82 | self.__data[index] = value 83 | end, 84 | } 85 | LuaScriptInstance = ffi_metatype('lps_script_instance', { 86 | --- `Object` that this script instance is attached to. 87 | -- This is the Godot side of the instance. 88 | -- @tfield Object __owner 89 | 90 | --- `LuaScript` for instance, the one returned by the Lua script when 91 | -- loading it as a PluginScript. Note that calling `Object:get_script` will 92 | -- return an `Object` rather than this wrapper. 93 | -- @tfield LuaScript __script 94 | 95 | --- `LuaObject` that references an internal table for holding arbitrary data. 96 | -- @tfield LuaObject __data 97 | 98 | --- Try indexing `__owner`, then `__data`, then `__script`. 99 | -- @function __index 100 | -- @param key 101 | -- @return 102 | -- @see Object.__index 103 | __index = function(self, key) 104 | local value = methods[key] 105 | if is_not_nil(value) then return value end 106 | local script_value = self.__script[key] 107 | if type(script_value) == 'function' then return script_value end 108 | value = self.__owner[key] 109 | if is_not_nil(value) then return value end 110 | value = self.__data[key] 111 | if is_not_nil(value) then return value end 112 | return script_value 113 | end, 114 | --- Calls `Object:set` if `key` is the name of a property known to base class, `rawset` otherwise. 115 | -- @function __newindex 116 | -- @param key 117 | -- @param value 118 | __newindex = function(self, key, value) 119 | if self.__script:has_property(key) then 120 | Object_set(self.__owner, key, value) 121 | else 122 | self:rawset(key, value) 123 | end 124 | end, 125 | --- Returns a Lua string representation of `__owner`, as per `Object:to_string`. 126 | -- @function __tostring 127 | -- @treturn string 128 | __tostring = function(self) 129 | return tostring(self.__owner) 130 | end, 131 | --- Concatenates values. 132 | -- @function __concat 133 | -- @param a First value, stringified with `GD.str` 134 | -- @param b First value, stringified with `GD.str` 135 | -- @treturn String 136 | __concat = concat_gdvalues, 137 | }) 138 | -------------------------------------------------------------------------------- /src/pluginscript_script.lua: -------------------------------------------------------------------------------- 1 | -- @file pluginscript_script.lua Script metadata struct and metatype 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Internal struct that wrap scripts with metadata. 25 | -- 26 | -- typedef struct { 27 | -- godot_string_name __path; 28 | -- godot_string_name __base; 29 | -- lps_lua_object __properties; 30 | -- lps_lua_object __implementation; 31 | -- } lps_lua_script; 32 | -- 33 | -- @module LuaScript 34 | 35 | ffi_cdef[[ 36 | typedef struct { 37 | godot_string_name __path; 38 | godot_string_name __base; 39 | lps_lua_object __properties; 40 | lps_lua_object __implementation; 41 | } lps_lua_script; 42 | ]] 43 | 44 | --- Allocs and returns a pointer to a `LuaScript`. 45 | -- @param path Script path 46 | -- @tparam string|nil base Base class name. Defaults to "Reference". 47 | -- @tparam table properties Known properties from script 48 | -- @tparam table implementation Script implementation 49 | -- @treturn LuaScript 50 | local function LuaScript_new(path, base, properties, implementation) 51 | local self = ffi_cast('lps_lua_script *', api.godot_alloc(ffi_sizeof('lps_lua_script'))) 52 | self.__path = ffi_gc(StringName(path), nil) 53 | self.__base = ffi_gc(StringName(base or 'Reference'), nil) 54 | self.__properties = LuaObject(properties) 55 | self.__implementation = LuaObject(implementation) 56 | return self 57 | end 58 | 59 | --- Frees all memory associated with a `LuaScript`. 60 | -- @tparam LuaScript self 61 | local function LuaScript_destroy(self) 62 | api.godot_string_name_destroy(self.__path) 63 | LuaObject_destroy(self.__properties) 64 | LuaObject_destroy(self.__implementation) 65 | api.godot_free(self) 66 | end 67 | 68 | --- @type LuaScript 69 | local methods = { 70 | --- Returns whether this script or its base class has a property named `name`. 71 | -- @function has_property 72 | -- @tparam string name Property name 73 | -- @treturn bool 74 | has_property = function(self, name) 75 | return self.__properties[name] ~= nil 76 | or ClassWrapper_cache[self.__base]:has_property(name) 77 | end, 78 | } 79 | local LuaScript = ffi_metatype('lps_lua_script', { 80 | --- Script path, usually relative to `res://` 81 | -- @tfield StringName __path 82 | 83 | --- Base class name 84 | -- @tfield StringName __base 85 | 86 | --- Table of known properties 87 | -- @tfield LuaObject __properties 88 | 89 | --- Table returned from script file, holding method implementations 90 | -- @tfield LuaObject __implementation 91 | 92 | --- Forwards indexing to script implementation 93 | -- @function __index 94 | -- @param index 95 | -- @return Value 96 | __index = function(self, index) 97 | return methods[index] 98 | or self.__implementation[index] 99 | end, 100 | }) 101 | -------------------------------------------------------------------------------- /src/pluginscript_signal.lua: -------------------------------------------------------------------------------- 1 | -- @file pluginscript_signal.lua Signal declarations for scripts 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | 24 | --- Signal declarations for scripts. 25 | -- @module signal 26 | 27 | --- Signal metatable, used only to check if a value is a signal. 28 | local Signal = {} 29 | 30 | --- Checks if `value` is a signal. 31 | local function is_signal(value) 32 | return getmetatable(value) == Signal 33 | end 34 | 35 | --- Transforms a `Signal` into a `Dictionary`, for populating scripts metadata. 36 | local function signal_to_dictionary(sig) 37 | local args = Array() 38 | for i = 1, #sig do 39 | args:append(Dictionary{ name = String(sig[i]) }) 40 | end 41 | local dict = Dictionary() 42 | dict.args = args 43 | return dict 44 | end 45 | 46 | --- Create a signal table, only useful for declaring scripts' signals. 47 | -- @usage 48 | -- MyClass.something_happened = signal() 49 | -- MyClass.something_happened_with_args = signal('arg1', 'arg2', 'etc') 50 | -- @param ... Signal argument names 51 | -- @treturn table 52 | -- @see lps_coroutine.lua 53 | function signal(...) 54 | return setmetatable({ ... }, Signal) 55 | end 56 | -------------------------------------------------------------------------------- /src/register_in_editor_callbacks.lua: -------------------------------------------------------------------------------- 1 | -- @file register_in_editor_callbacks.lua Entrypoint for registering in editor callbacks 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021-2023 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | if in_editor then 24 | xpcall(require, GD.print_error, 'addons/godot-lua-pluginscript/plugin/in_editor_callbacks') 25 | end 26 | -------------------------------------------------------------------------------- /src/test/array.lua: -------------------------------------------------------------------------------- 1 | local lu = require "luaunit" 2 | 3 | local Test = {} 4 | 5 | function Test:test_array_index() 6 | local arr = Array(1, 3.14, 'hello!') 7 | lu.assert_nil(arr[0]) 8 | lu.assert_equals(1, arr[1]) 9 | lu.assert_equals(3.14, arr[2]) 10 | lu.assert_equals(String 'hello!', arr[3]) 11 | lu.assert_nil(arr[4]) 12 | end 13 | 14 | function Test:test_array_newindex() 15 | local arr = Array() 16 | arr[1] = 1 17 | arr[2] = '2' 18 | 19 | lu.assert_equals(1, arr[1]) 20 | lu.assert_equals(String '2', arr[2]) 21 | lu.assert_equals(2, #arr) 22 | 23 | arr[1] = 2 24 | arr[2] = '3' 25 | 26 | lu.assert_equals(2, arr[1]) 27 | lu.assert_equals(String '3', arr[2]) 28 | lu.assert_equals(2, #arr) 29 | end 30 | 31 | function Test:sort(a, b) 32 | return a < b 33 | end 34 | function Test:test_sort() 35 | local arr = Array(1, 6, 3, 7, 3) 36 | arr:sort_custom(self, 'sort') 37 | lu.assert_equals(Array(1, 3, 3, 6, 7), arr) 38 | end 39 | 40 | return Test 41 | -------------------------------------------------------------------------------- /src/test/class_wrapper.lua: -------------------------------------------------------------------------------- 1 | local lu = require "luaunit" 2 | 3 | local Test = {} 4 | 5 | function Test:test_has_property() 6 | lu.assert_true(Node:has_property 'name') 7 | lu.assert_false(Node:has_property 'not_a_property') 8 | end 9 | 10 | function Test:test_inherits() 11 | lu.assert_true(Node:inherits 'Object') 12 | lu.assert_true(Node2D:inherits 'Object') 13 | lu.assert_false(Node:inherits 'not_a_class') 14 | end 15 | 16 | function Test:test_get_parent_class() 17 | lu.assert_equals(String 'Object', Node:get_parent_class()) 18 | lu.assert_equals(String 'CanvasItem', Node2D:get_parent_class()) 19 | end 20 | 21 | function Test:test_ReturnsConstant() 22 | lu.assert_equals(1, Object.CONNECT_DEFERRED) 23 | lu.assert_equals(1, File.READ) 24 | lu.assert_equals(2, File.WRITE) 25 | end 26 | 27 | return Test 28 | -------------------------------------------------------------------------------- /src/test/coroutines.lua: -------------------------------------------------------------------------------- 1 | local lu = require "luaunit" 2 | 3 | local Test = {} 4 | 5 | function Test:return_yield() 6 | return GD.yield() 7 | end 8 | function Test:test_yield_results() 9 | local coro = self:call('return_yield') 10 | lu.assert_nil(coro:resume()) 11 | 12 | local coro = self:call('return_yield') 13 | lu.assert_equals(coro:resume(42), 42) 14 | end 15 | 16 | function Test:return_yield_numbers(max) 17 | for i = 1, max do 18 | GD.yield() 19 | end 20 | end 21 | function Test:test_yield_more_than_once() 22 | local max = 10 23 | local coro = self:call('return_yield_numbers', max) 24 | local i = 0 25 | while coro:is_valid() do 26 | coro:resume() 27 | i = i + 1 28 | end 29 | lu.assert_equals(i, max) 30 | end 31 | 32 | return Test 33 | -------------------------------------------------------------------------------- /src/test/extras/invalid_extends.lua: -------------------------------------------------------------------------------- 1 | return { 2 | extends = 'invalid class', 3 | } 4 | -------------------------------------------------------------------------------- /src/test/extras/parse_error.lua: -------------------------------------------------------------------------------- 1 | hello, this is not a Lua script =O 2 | -------------------------------------------------------------------------------- /src/test/extras/valid_script.lua: -------------------------------------------------------------------------------- 1 | local Script = { 2 | extends = 'Node' 3 | } 4 | 5 | return Script 6 | -------------------------------------------------------------------------------- /src/test/extras/valid_script_class_wrapper.lua: -------------------------------------------------------------------------------- 1 | local Script = { 2 | extends = Node 3 | } 4 | 5 | return Script 6 | 7 | -------------------------------------------------------------------------------- /src/test/init.lua: -------------------------------------------------------------------------------- 1 | -- @file test/init.lua Unit test runner 2 | -- This file is part of Godot Lua PluginScript: https://github.com/gilzoide/godot-lua-pluginscript 3 | -- 4 | -- Copyright (C) 2021 Gil Barbosa Reis. 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 7 | -- of this software and associated documentation files (the “Software”), to 8 | -- deal in the Software without restriction, including without limitation the 9 | -- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | -- sell copies of the Software, and to permit persons to whom the Software is 11 | -- furnished to do so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in 14 | -- all copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | -- IN THE SOFTWARE. 23 | local TestRunner = { 24 | extends = 'SceneTree', 25 | } 26 | 27 | local function error_handler(msg) 28 | if os.getenv('DEBUG_INTERACTIVE') then 29 | local have_dbg, dbg = pcall(require, 'debugger') 30 | if have_dbg then 31 | return dbg() 32 | end 33 | end 34 | GD.print_error(msg) 35 | end 36 | 37 | function TestRunner:setup_luapath(current_script_base_dir) 38 | local additional_paths = { '../../lib/luaunit', '../../lib/debugger_lua' } 39 | for _, path in ipairs(additional_paths) do 40 | local additional_path = current_script_base_dir:plus_file(path) 41 | package.path = string.format('%s/?.lua;%s/?/init.lua;%s', additional_path, additional_path, package.path) 42 | end 43 | end 44 | 45 | function TestRunner:setup_cpath() 46 | local additional_paths 47 | if OS:get_name() == 'Windows' then 48 | additional_paths = { 49 | '!/addons/godot-lua-pluginscript/build/windows_x86/?.dll', 50 | '!/addons/godot-lua-pluginscript/build/windows_x86_64/?.dll', 51 | } 52 | elseif OS:get_name() == 'OSX' then 53 | additional_paths = { 54 | '!/addons/godot-lua-pluginscript/build/osx_arm64_x86_64/?.dylib', 55 | } 56 | else 57 | additional_paths = { 58 | '!/addons/godot-lua-pluginscript/build/linux_x86/?.so', 59 | '!/addons/godot-lua-pluginscript/build/linux_x86_64/?.so', 60 | } 61 | end 62 | 63 | for _, path in ipairs(additional_paths) do 64 | package.cpath = string.format('%s;%s', path, package.cpath) 65 | end 66 | end 67 | 68 | function TestRunner:_init() 69 | local current_script_path = self:get_script().resource_path 70 | local current_script_filename = current_script_path:get_file() 71 | local current_script_base_dir = current_script_path:get_base_dir() 72 | 73 | self:setup_luapath(current_script_base_dir) 74 | self:setup_cpath() 75 | 76 | local dir, all_passed = Directory:new(), true 77 | assert(dir:open(current_script_base_dir) == GD.OK) 78 | dir:list_dir_begin(true) 79 | repeat 80 | local filename = dir:get_next() 81 | if filename:ends_with('.lua') and filename ~= current_script_filename then 82 | local script = GD.load(current_script_base_dir:plus_file(filename)) 83 | local instance = script:new() 84 | if instance:is_class('Node') then 85 | self.root:add_child(instance) 86 | end 87 | local lua_instance = GD.get_lua_instance(instance) 88 | print(string.format('> %s:', filename)) 89 | for i, method in ipairs(script:get_script_method_list()) do 90 | if method.name:begins_with("test") then 91 | local success = xpcall(lua_instance[tostring(method.name)], error_handler, lua_instance) 92 | print(string.format(' %s %s: %s', success and '✓' or '🗴', method.name, success and 'passed' or 'failed')) 93 | all_passed = all_passed and success 94 | end 95 | end 96 | instance:pcall('queue_free') 97 | end 98 | until filename == '' 99 | dir:list_dir_end() 100 | 101 | self:quit(all_passed and 0 or 1) 102 | end 103 | 104 | return TestRunner 105 | -------------------------------------------------------------------------------- /src/test/require_luac.lua: -------------------------------------------------------------------------------- 1 | local lu = require "luaunit" 2 | 3 | local Test = {} 4 | 5 | function Test:test_require_luac_module() 6 | lu.assert_true(pcall(require, 'test_cmodule')) 7 | end 8 | 9 | return Test 10 | -------------------------------------------------------------------------------- /src/test/script_loading.lua: -------------------------------------------------------------------------------- 1 | local lu = require "luaunit" 2 | 3 | local Test = {} 4 | 5 | function Test:get_extra_script(name) 6 | local script_path = self:get_script().resource_path:get_base_dir():plus_file('extras'):plus_file(name) 7 | return GD.load(script_path) 8 | end 9 | 10 | function Test:test_WhenValidScript_CanInstance() 11 | local script = self:get_extra_script("valid_script.lua") 12 | lu.assert_true(script:can_instance()) 13 | end 14 | 15 | function Test:test_WhenExtendsClassWrapper_IsValidAndCanInstance() 16 | local script = self:get_extra_script("valid_script_class_wrapper.lua") 17 | lu.assert_true(script:can_instance()) 18 | end 19 | 20 | function Test:test_WhenParseError_CantInstance() 21 | local script = self:get_extra_script("parse_error.lua") 22 | lu.assert_false(script:can_instance()) 23 | end 24 | 25 | function Test:test_WhenExtendsIsNotAClassNorScriptPath_CantInstance() 26 | local script = self:get_extra_script("invalid_extends.lua") 27 | lu.assert_false(script:can_instance()) 28 | end 29 | 30 | return Test 31 | -------------------------------------------------------------------------------- /src/test/setter_newindex.lua: -------------------------------------------------------------------------------- 1 | local lu = require "luaunit" 2 | 3 | local Test = { 4 | extends = 'Node' 5 | } 6 | 7 | Test.custom_prop = property { 8 | default = false, 9 | set = function(self, value) 10 | self:rawset('custom_prop', value) 11 | self.custom_prop_setter_called = true 12 | end, 13 | } 14 | 15 | function Test:test_WhenSettingPropertyInClass_SetterIsCalled() 16 | local name = String 'test' 17 | self.name = name 18 | lu.assert_equals(self.name, name) 19 | lu.assert_equals(self:get 'name', name) 20 | lu.assert_nil(self:rawget 'name') 21 | end 22 | 23 | function Test:test_WhenSettingPropertyInScript_SetterIsCalled() 24 | self.custom_prop = true 25 | lu.assert_true(self.custom_prop, true) 26 | lu.assert_true(self.custom_prop_setter_called, true) 27 | end 28 | 29 | return Test 30 | -------------------------------------------------------------------------------- /src/test/test_cmodule.c: -------------------------------------------------------------------------------- 1 | #include "lua.h" 2 | 3 | int luaopen_test_cmodule(lua_State *L) { 4 | lua_pushboolean(L, 1); 5 | return 1; 6 | } 7 | -------------------------------------------------------------------------------- /src/tools/add_script_c_decl.sed: -------------------------------------------------------------------------------- 1 | # NOTE: using actual newlines instead of "\n" for BSD/OSX version of sed 2 | # Text before first line 3 | 1 i\ 4 | #include\ 5 | const char LUA_INIT_SCRIPT[] = 6 | # Text after last line 7 | $ a\ 8 | ;\ 9 | const size_t LUA_INIT_SCRIPT_SIZE = sizeof(LUA_INIT_SCRIPT); 10 | -------------------------------------------------------------------------------- /src/tools/compact_c_ffi.sed: -------------------------------------------------------------------------------- 1 | /cdef\[\[/,/\]\]/ { 2 | # Remove C comments 3 | s/[[:space:]]*\/\/.*// 4 | # Minify unused private fields 5 | s/_dont_touch_that/_/ 6 | # Remove function parameter names 7 | /\(\*/ { 8 | s/[[:space:]]*[_a-zA-Z0-9]*(,|\);)/\1/g 9 | } 10 | # Remove unused enum/struct/union names when they're typedef'd 11 | s/typedef (enum|struct|union) [_a-zA-Z0-9]*/typedef \1/ 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/tools/embed_to_c.sed: -------------------------------------------------------------------------------- 1 | # Escape backslashes 2 | s/\\/\\\\/g 3 | # Escape quotes 4 | s/"/\\"/g 5 | # Add starting quote 6 | s/^/"/ 7 | # Add ending newline, except on last script line 8 | $! s/$/\\n/ 9 | # Add ending quote 10 | s/$/"/ 11 | -------------------------------------------------------------------------------- /src/tools/project.godot: -------------------------------------------------------------------------------- 1 | ; Godot project config file for unit test runner 2 | 3 | config_version=4 4 | 5 | [application] 6 | 7 | config/name="godot-lua-pluginscript-unittest" 8 | 9 | [gdnative] 10 | 11 | singletons=[ "res://addons/godot-lua-pluginscript/lua_pluginscript.gdnlib" ] 12 | 13 | [logging] 14 | 15 | file_logging/enable_file_logging.pc=false 16 | file_logging/log_path="testlogdir" 17 | 18 | [rendering] 19 | 20 | quality/driver/driver_name="GLES2" 21 | -------------------------------------------------------------------------------- /src/tools/remove_lua_comments.sed: -------------------------------------------------------------------------------- 1 | # Remove all comment lines but the first license notice and "-- @file ..." lines 2 | 25,$ { 3 | # Ensure there is a new line before "-- @file ..." lines 4 | s/(-- @file)/\n\1/ 5 | # Skip next command if the previous one substituted anything 6 | # That is, when current line is a "-- @file ..." line, skip everything below 7 | t 8 | # Remove comment lines 9 | /^[[:space:]]*--/d 10 | } 11 | -------------------------------------------------------------------------------- /src/tools/squeeze_blank_lines.sed: -------------------------------------------------------------------------------- 1 | # On empty lines, do: 2 | /^$/ { 3 | : remove-empty-line 4 | # Load the next input line 5 | N 6 | # Remove last line, if it is empty 7 | s/\n$// 8 | # In case last line was empty, repeat the process to squeeze multiple empty lines into a single one 9 | t remove-empty-line 10 | } 11 | 12 | --------------------------------------------------------------------------------