Support Roblox-like requires, using a `script` global.
Closed this issue · 3 comments
Already been discussed in Discord, but creating an issue for formality.
Lune could be an excellent option for unit-testing Roblox scripts outside a Roblox environment. If scripts are decoupled from engine APIs, there's no reason they shouldn't be able to run in an environment like Lune. Lune could support Roblox-like require
s as an opt-in feature, using a Rojo source map to resolve file paths.
In the script environment, this would require a few things:
- A new
Instance
userdata which allows for indexing children and getting the parent. Specific instance types shouldn't be necessary; there just needs to be a way to traverse the datamodel. - A
script
global, which is simply the current script'sInstance
. - A
game
global, anInstance
which represents the root of the datamodel.- How much functionality should be provided here? To maintain compatibility with most Roblox scripts,
GetService
should be supported so that datamodel services can be indexed in an idiomatic way.
- How much functionality should be provided here? To maintain compatibility with most Roblox scripts,
- An alternative
require
implementation which supports referencingInstance
s. Much of the existing asyncrequire
logic can be reused.
In addition, under the existing roblox
global, Lune could support scripts implementing functionality for Roblox services. Lune can't reimplement everything (and it shouldn't aim to), but allowing users to reimplement what they need would be very powerful. Something like this:
roblox.implementService("HttpService", function(service)
service:AddMethod("JSONEncode", ...)
end)
One thing to consider is how this pairs with the existing DataModel implementation for reading place/model files. How much functionality there can be reused?
This is probably something that will take some time to get right - but now that the general Lune runtime and its built-in libraries are starting to stabilize I'm definitely more open to working towards helping more specific usecases such as this one. Some general thoughts I have right now are, in no particular order:
Roblox globals
We could add a function like injectRobloxGlobals
in the roblox
library which would make the script
and game
globals available after the point in time that function is called. Maybe we could provide a general solution for this - injecting something like require("@lune/task")
as a task
global would suddenly make Lune work with a lot of signal & promise libraries out there
Roblox-style requires
The script
and game
globals for require-purposes could probably share most if not all of the existing require functionality, we could use a trait such as IntoRequirePath
that anything require should support implements. Instance
on the new roblox lib is also completely independent of reading/writing actual roblox files, you can use it without, so we might be able to share some stuff here too
Implementing service / class methods
I really like the idea of letting users implement class methods if they choose to, there are quite a few methods such as Model::PivotTo
that are super useful in the context of Lune (imagine a script for a tycoon-like game that duplicates and places the tycoon plot at build-time instead of at runtime or manually) but that are definitely out of scope for the runtime itself. I also think this point specifically may be a lot more straightforward to design for & implement compared to the others, we could do this soon-ish
Did some work to start supporting this in 9fe3b02 - the plan goes something like this:
-
Use the
luau
built-in andluau.load
with custom environment andrequire
function set, to override default behavior -
This custom
require
function (implemented by the user, in lua) can support whatever they need, be it sourcemaps, mixing instance and string-based requires, ... -
Properties and methods can be implemented in lua and added using:
roblox.implementProperty(className: string, propertyName: string, getter: function, setter: function?)
roblox.implementMethod(className: string, methodName: string, callback: function)
where all of the functions take the instance as the first argument, and values as the rest. These will also respect instance class hierarchies and try to call a method for
BasePart
when one forPart
is not available, etc.
I think this is really all we need to make workflows such as unit testing viable, and that is the major goal for 0.8.0
, so I'll add back the milestone and close this issue once the functions on the roblox
built-in are available.
The abovementioned APIs on the roblox
built-in are now available as of version 0.7.9
!