Skip to content

Commit 8afeab1

Browse files
committed
Add shortcut for Resource properties
1 parent 052a4a3 commit 8afeab1

File tree

6 files changed

+145
-64
lines changed

6 files changed

+145
-64
lines changed

README.md

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,12 @@ as a scripting language in [Godot](https://godotengine.org/).
66
Being a GDNative library, recompiling the engine is not required, so anyone
77
with a built release copied to their project can use it.
88
Being a PluginScript language, Lua can seamlessly communicate with scripts
9-
written in GDScript/C#/Visual Script and vice-versa.
9+
written in GDScript / C# / Visual Script and vice-versa.
10+
This way, one can use the language that best suits the implementation for each
11+
script and all of them can understand each other.
1012

11-
12-
## Documentation
13-
The API is documented using [LDoc](https://stevedonovan.github.io/ldoc/manual/doc.md.html)
14-
and is available online at [github pages](https://gilzoide.github.io/godot-lua-pluginscript/topics/README.md.html).
15-
16-
Generate the documentation with the following command:
17-
18-
# make docs
13+
Currently, only LuaJIT is supported, since the implementation is based on its
14+
[FFI](https://luajit.org/ext_ffi.html) library.
1915

2016

2117
## Installing
@@ -25,14 +21,6 @@ Make sure the `lua_pluginscript.gdnlib` file is located at the
2521
`res://addons/godot-lua-pluginscript` folder.
2622

2723

28-
## Articles
29-
30-
1. [Designing Godot Lua PluginScript](blog/1-design-en.md)
31-
2. [Implementing the library's skeleton](blog/2-infrastructure-en.md)
32-
3. [Integrating LuaJIT and FFI](blog/3-luajit-callbacks-en.md)
33-
4. Initializing and finalizing scripts (TODO)
34-
35-
3624
## Goals
3725

3826
- Provide support for Lua as a scripting language in Godot in a way that does
@@ -41,7 +29,7 @@ Make sure the `lua_pluginscript.gdnlib` file is located at the
4129
like GDScript, Visual Script and C#, in an idiomatic way
4230
- Simple script description interface that doesn't need `require`ing anything
4331
- Support for LuaJIT and Lua 5.2+
44-
- Support paths relative to `res://*` and exported game executable path for
32+
- Support paths relative to `res://*` and exported game/app executable path for
4533
`require`ing Lua modules
4634
- Have a simple build process, where anyone with the cloned source code and
4735
installed build system + toolchain can build the project in a single step
@@ -53,6 +41,23 @@ Make sure the `lua_pluginscript.gdnlib` file is located at the
5341
- Support multithreading on the Lua side
5442

5543

44+
## Documentation
45+
The API is documented using [LDoc](https://stevedonovan.github.io/ldoc/manual/doc.md.html)
46+
and is available online at [github pages](https://gilzoide.github.io/godot-lua-pluginscript/topics/README.md.html).
47+
48+
Documentation may be generated with the following command:
49+
50+
# make docs
51+
52+
53+
## Articles
54+
55+
1. [Designing Godot Lua PluginScript](https://github.com/gilzoide/godot-lua-pluginscript/blob/main/blog/1-design-en.md)
56+
2. [Implementing the library's skeleton](https://github.com/gilzoide/godot-lua-pluginscript/blob/main/blog/2-infrastructure-en.md)
57+
3. [Integrating LuaJIT and FFI](https://github.com/gilzoide/godot-lua-pluginscript/blob/main/blog/3-luajit-callbacks-en.md)
58+
4. Initializing and finalizing scripts (TODO)
59+
60+
5661
## Script example
5762

5863
This is an example of how a Lua script looks like. There are comments regarding
@@ -81,37 +86,32 @@ MyClass.some_prop = 42
8186
-- The `property` function adds metadata to defined properties,
8287
-- like setter and getter functions
8388
MyClass.some_prop_with_details = property {
84-
-- [1] or ["default"] or ["default_value"] = property default value
89+
-- ["default_value"] or ["default"] or [1] = property default value
8590
5,
86-
-- [2] or ["type"] = variant type, optional, inferred from default value
91+
-- ["type"] or [2] = variant type, optional, inferred from default value
8792
-- All Godot variant type names are defined globally as written in
8893
-- GDScript, like bool, int, float, String, Array, Vector2, etc...
8994
-- Notice that Lua <= 5.2 does not differentiate integers from float
9095
-- numbers, so we should always specify `int` where appropriate
9196
-- or use `int(5)` in the default value instead
9297
type = int,
93-
-- ["set"] or ["setter"] = setter function, optional
94-
set = function(self, value)
95-
self.some_prop_with_details = value
96-
-- Indexing `self` with keys undefined in script will search base
97-
-- class for methods and properties
98-
self:emit_signal("something_happened_with_args", "some_prop_with_details", value)
99-
end,
100-
-- ["get"] or ["getter"] = getter function, optional
98+
-- ["get"] or ["getter"] = getter function or method name, optional
10199
get = function(self)
102100
return self.some_prop_with_details
103101
end,
104-
-- ["usage"] = property usage, from enum godot_property_usage_flags
105-
-- optional, default to GD.PROPERTY_USAGE_DEFAULT
106-
usage = GD.PROPERTY_USAGE_DEFAULT,
107-
-- ["hint"] = property hint, from enum godot_property_hint
108-
-- optional, default to GD.PROPERTY_HINT_NONE
109-
hint = GD.PROPERTY_HINT_RANGE,
102+
-- ["set"] or ["setter"] = setter function or method name, optional
103+
set = 'set_some_prop_with_details',
104+
-- ["usage"] = property usage, from `enum godot_property_usage_flags`
105+
-- optional, default to `PropertyUsage.DEFAULT`
106+
usage = PropertyUsage.DEFAULT,
107+
-- ["hint"] = property hint, from `enum godot_property_hint`
108+
-- optional, default to `PropertyHint.NONE`
109+
hint = PropertyHint.RANGE,
110110
-- ["hint_string"] = property hint text, only required for some hints
111111
hint_string = '1,10',
112-
-- ["rset_mode"] = property remote set mode, from enum godot_method_rpc_mode
113-
-- optional, default to GD.RPC_MODE_DISABLED
114-
rset_mode = GD.RPC_MODE_MASTER,
112+
-- ["rset_mode"] = property remote set mode, from `enum godot_method_rpc_mode`
113+
-- optional, default to `RPCMode.DISABLED`
114+
rset_mode = RPCMode.MASTER,
115115
}
116116

117117
-- Functions defined in table are public methods
@@ -121,7 +121,14 @@ function MyClass:_ready() -- `function t:f(...)` is an alias for `function t.f(
121121
print("MyClass instance is ready! Running on a " .. os_name .. " system")
122122
end
123123

124-
function MyClass:some_prop_doubled()
124+
function MyClass:set_some_prop_with_details(value)
125+
self.some_prop_with_details = value
126+
-- Indexing `self` with keys undefined in script will search base
127+
-- class for methods and properties
128+
self:emit_signal("something_happened_with_args", "some_prop_with_details", value)
129+
end
130+
131+
function MyClass:get_some_prop_doubled()
125132
return self.some_prop * 2
126133
end
127134

src/godot_class.lua

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,19 @@ local class_methods = {
8080
Object_call(obj, '_init', ...)
8181
return obj
8282
end,
83+
--- Returns whether this Class inherits a `parent` Class.
84+
-- @function inherits
85+
-- @param other Other class name
86+
-- @treturn bool
87+
inherits = function(self, parent)
88+
return ClassDB:is_parent_class(self.class_name, parent)
89+
end,
90+
--- Returns the parent class.
91+
-- @function get_parent_class
92+
-- @treturn String
93+
get_parent_class = function(self)
94+
return ClassDB:get_parent_class(self.class_name)
95+
end,
8396
}
8497
local ClassWrapper = {
8598
new = function(self, class_name)
@@ -114,8 +127,18 @@ local ClassWrapper = {
114127
return constant
115128
end
116129
end,
130+
--- Returns `class_name`
131+
-- @function __tostring
132+
-- @treturn string
133+
__tostring = function(self)
134+
return self.class_name
135+
end,
117136
}
118137

138+
local function is_class_wrapper(v)
139+
return getmetatable(v) == ClassWrapper
140+
end
141+
119142
--- MethodBind metatype, wrapper for `godot_method_bind`.
120143
-- These are returned by `ClassWrapper:__index` and GDNative's `godot_method_bind_get_method`.
121144
-- @type MethodBind

src/godot_enums.lua

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -241,19 +241,19 @@ for k, v in pairs(RPCMode) do RPCMode[v] = k end
241241
--- Enum `godot_property_hint`, mapping UPPER_CASED names to ordinal values and vice-versa.
242242
-- @table PropertyHint
243243
-- @field NONE None
244-
-- @field RANGE Range
245-
-- @field EXP_RANGE Exp Range
246-
-- @field ENUM Enum
244+
-- @field RANGE Range (hint_string = `"min,max,step,slider"`; step and slider are optional)
245+
-- @field EXP_RANGE Exp Range (hint_string = `"min,max,step"`; exponential edit)
246+
-- @field ENUM Enum (hint_string = `"val1,val2,val3,etc"`)
247247
-- @field EXP_EASING Exp Easing
248-
-- @field LENGTH Length
249-
-- @field SPRITE_FRAME Sprite Frame
250-
-- @field KEY_ACCEL Key Accel
251-
-- @field FLAGS Flags
248+
-- @field LENGTH Length (hint_string = `"length"`; as integer)
249+
-- @field SPRITE_FRAME Sprite Frame (Obsolete)
250+
-- @field KEY_ACCEL Key Accel (hint_string = `"length"`; as integer)
251+
-- @field FLAGS Flags (hint_string = `"flag1,flag2,etc"`; as bit flags)
252252
-- @field LAYERS_2D_RENDER Layers 2D Render
253253
-- @field LAYERS_2D_PHYSICS Layers 2D Physics
254254
-- @field LAYERS_3D_RENDER Layers 3D Render
255255
-- @field LAYERS_3D_PHYSICS Layers 3D Physics
256-
-- @field FILE File
256+
-- @field FILE File (optional hint_string: file filter, e.g. `"\*.png,\*.wav"`)
257257
-- @field DIR Dir
258258
-- @field GLOBAL_FILE Global File
259259
-- @field GLOBAL_DIR Global Dir
@@ -264,7 +264,7 @@ for k, v in pairs(RPCMode) do RPCMode[v] = k end
264264
-- @field IMAGE_COMPRESS_LOSSY Image Compress Lossy
265265
-- @field IMAGE_COMPRESS_LOSSLESS Image Compress Lossless
266266
-- @field OBJECT_ID Object Id
267-
-- @field TYPE_STRING Type String
267+
-- @field TYPE_STRING Type String (hint_string = `"base class name"`)
268268
-- @field NODE_PATH_TO_EDITED_NODE Node Path To Edited Node
269269
-- @field METHOD_OF_VARIANT_TYPE Method Of Variant Type
270270
-- @field METHOD_OF_BASE_TYPE Method Of Base Type
@@ -323,8 +323,8 @@ for k, v in pairs(PropertyHint) do PropertyHint[v] = k end
323323
-- @field INTERNATIONALIZED Internationalized
324324
-- @field GROUP Group
325325
-- @field CATEGORY Category
326-
-- @field STORE_IF_NONZERO Store If Nonzero
327-
-- @field STORE_IF_NONONE Store If Nonone
326+
-- @field STORE_IF_NONZERO Store If Nonzero (Obsolete)
327+
-- @field STORE_IF_NONONE Store If Nonone (Obsolete)
328328
-- @field NO_INSTANCE_STATE No Instance State
329329
-- @field RESTART_IF_CHANGED Restart If Changed
330330
-- @field SCRIPT_VARIABLE Script Variable

src/godot_string_name.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
-- IN THE SOFTWARE.
2323

2424
--- StringName metatype, wrapper for `godot_string_name`.
25-
-- Construct using the idiom `StringName(...)`, which calls `__new`.
25+
-- Construct using the idiom `StringName(name)`, which calls `__new`.
2626
-- @classmod StringName
2727

2828
local methods = {

src/pluginscript_callbacks.lua

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,10 @@ clib.lps_script_init_cb = wrap_callback(function(manifest, path, source, err)
128128
prop.name = String(k)
129129
-- Maintain default value directly for __indexing
130130
metadata[k] = default_value
131-
-- TODO: support strings for get/set
132131
if get then
133-
if is_a_string(get) then
134-
get = MethodBindByName:new(get)
135-
end
136132
getter[k] = get
137133
end
138134
if set then
139-
if is_a_string(set) then
140-
set = MethodBindByName:new(set)
141-
end
142135
setter[k] = set
143136
end
144137
manifest.properties:append(prop)

src/pluginscript_class_metadata.lua

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2222
-- IN THE SOFTWARE.
2323

24-
-- Map names and ctypes to godot_variant_type
24+
--- Scripts metadata.
25+
-- This includes properties, signals and methods.
26+
-- @module script_metadata
27+
28+
-- Map names and types to godot_variant_type
2529
local property_types = {
2630
bool = VariantType.Bool, [bool] = VariantType.Bool,
2731
int = VariantType.Int, [int] = VariantType.Int,
@@ -85,23 +89,69 @@ local function property_to_dictionary(prop)
8589
default_value = prop
8690
dict.type = get_property_type(prop)
8791
else
88-
default_value = prop[1] or prop.default or prop.default_value
89-
local explicit_type = prop[2] or prop.type
90-
dict.type = property_types[explicit_type] or explicit_type or get_property_type(default_value)
91-
dict.hint = prop.hint
92-
dict.hint_string = prop.hint_string
92+
default_value = prop.default_value or prop.default or prop[1]
93+
local explicit_type = prop.type or prop[2]
94+
if is_class_wrapper(explicit_type) and explicit_type:inherits('Resource') then
95+
dict.type = VariantType.Object
96+
dict.hint = PropertyHint.RESOURCE_TYPE
97+
dict.hint_string = explicit_type.class_name
98+
else
99+
dict.type = property_types[explicit_type] or explicit_type or get_property_type(default_value)
100+
dict.hint = prop.hint
101+
dict.hint_string = prop.hint_string
102+
end
93103
dict.usage = prop.usage
94104
dict.rset_mode = prop.rset_mode
95105
get = prop.get or prop.getter
106+
if is_a_string(get) then
107+
get = MethodBindByName:new(get)
108+
end
96109
set = prop.set or prop.setter
110+
if is_a_string(set) then
111+
set = MethodBindByName:new(set)
112+
end
97113
end
98114
dict.default_value = default_value
99115
return dict, default_value, get, set
100116
end
101117

118+
--- Adds `metadata` to a property.
119+
-- If `metadata` is not a table, creates a table with this value as `default_value`.
120+
-- The `metadata` table may include the following fields:
121+
--
122+
-- * `default_value` or `default` or `1`: default property value, returned when
123+
-- Object has no other value set for it.
124+
-- * (*optional*) `type` or `2`: property type. If absent, it is inferred from `default_value`.
125+
-- May be a `Enumerations.VariantType` (`VariantType.Vector2`), the type directly (`Vector2`),
126+
-- or a Resource class (`AudioStream`)
127+
-- * (*optional*) `get` or `getter`: getter function. May be a string with the method name
128+
-- to be called or any callable value, like functions and tables with a `__call`
129+
-- metamethod.
130+
-- * (*optional*) `set` or `setter`: setter function. May be a string with the method name
131+
-- to be called or any callable value, like functions and tables with a `__call`
132+
-- metamethod.
133+
-- * (*optional*) `hint`: one of `Enumerations.PropertyHint`. Default to `PropertyHint.NONE`.
134+
-- * (*optional*) `hint_string`: the hint text, required for some hints like `RANGE`.
135+
-- * (*optional*) `usage`: one of `Enumerations.PropertyUsage`. Default to `PropertyUsage.DEFAULT`.
136+
-- * (*optional*) `rset_mode`: one of `Enumerations.RPCMode`. Default to `RPCMode.DISABLED`.
137+
--
138+
-- TODO: accept hints directly (`range = '1,10'`; `enum = 'value1,value2,value3'`; `file = '*.png'`, etc...).
139+
-- @usage
140+
-- MyClass.some_prop = property(42)
141+
-- MyClass.some_prop_with_metadata = property {
142+
-- type = int,
143+
-- set = function(self, value)
144+
-- self.some_prop_with_metadata = value
145+
-- self:emit('some_prop_with_metadata_changed', value)
146+
-- end,
147+
-- hint = PropertyHint.RANGE,
148+
-- hint_text = '1,100',
149+
-- }
150+
-- @treturn table
151+
-- @see lps_coroutine.lua
102152
function property(metadata)
103153
if type(metadata) ~= 'table' then
104-
metadata = { metadata }
154+
metadata = { default_value = metadata }
105155
end
106156
return setmetatable(metadata, Property)
107157
end
@@ -122,6 +172,14 @@ local function signal_to_dictionary(sig)
122172
return dict
123173
end
124174

175+
--- Create a signal table.
176+
-- This is only useful for declaring scripts' signals.
177+
-- @usage
178+
-- MyClass.something_happened = signal()
179+
-- MyClass.something_happened_with_args = signal('arg1', 'arg2', 'etc')
180+
-- @param ... Signal argument names
181+
-- @treturn table
182+
-- @see lps_coroutine.lua
125183
function signal(...)
126184
return setmetatable({ ... }, Signal)
127185
end

0 commit comments

Comments
 (0)