Events in SLua
planned
WolfGang Senizen
There has already been some discussion of this in other canny's
Like
But a centralized issue for feedback is probably a better idea.
As the post by Harold Linden says in the above link, LL are considering something like
LLEvents.touch_start = function ...
Personally I would rather suggest something more akin to
local handle = llevent.onTouchStart(function(touches:number) end)
local handle = llevent.dispose(handle)
or
local handle = llevent.on(llevent.TOUCH_START, function(touches:number) end)
llevent.dispose(handle)
or (nya's suggestion)
local function touchHandler(touches: number)
end
llevent.on(llevent.TOUCH_START, touchHandler)
llevent.off(llevent.TOUCH_START, touchHandler)
Mostly to allow for if not now, at least in the future multiple event handlers being setup and expanding to support things similar too
function listenHandler(channel, name, key, msg)
end
local listener = ll.Listen(0,"","","test")
llevent.on(llevent.LISTEN, listenHandler, listener)
or
function listenHandler(channel, name, key, msg)
end
llevent.on(llevent.LISTEN, listenHandler, {channel=0,message="test"})
Possibly something similar to roblox's "standard", or something designed in a way that is compatible with it, so it can be properly extended later.
This needs to happen BEFORE a possible beta phase
There should also be NO COMPATABILITY with the current way of working, all current scripts SHOULD break and need rewriting, having both is not really a good option, and NOW is the time for breaking that.
Log In
SuzannaLinn Resident
Will it work to have several timers?
Like:
llevent.Timer(updateClock, 1)
llevent.Timer(scanVisitors, 300)
H
Harold Linden
planned
We're going to get something in before beta, and we definitely intend on breaking the current way of setting event handlers since it's just a hack.
I think something akin to Roblox's
Connect()
API makes sense, it would make it a lot easier to have a defined handler signature in your type packs for each event type, and you can disconnect it directly using the handle object that gets returned https://create.roblox.com/docs/reference/engine/datatypes/RBXScriptConnectionIf you use an event emitter style API you potentially have to do a lot of dataflow analysis to figure out what function signature is expected for a given handler, unless you do matching on literal strings in first arg.
A Roblox-style API would trivially allow things like
local handle = llevents.TouchStart:Connect(function(num)
if num == 2 then
handle:Disconnect()
end
end)
Is there anything that'd tip the scales in favor of
handle
just being an opaque value that has to be passed to some other function, or in favor of taking the event name in as a string arg?WolfGang Senizen
Harold Linden
I think it should be ok to leave it upto the scripter to get the function signature right, and hardcoding the type of event based on the function called
TouchStart
or the argument passed would be fine.Event name as string arg is a nice "alternative" option, for slghtly easier dynamic scripting, it can sort of be achieved by doing
llevents["TouchStart"]:Connect
As for an opaque value, that depends on how much data gets assigned to the handle table, we are precious about our bytes, and what the handle "Object" can do going forward, for instance...
The ability to filter events better, has been asked for FOREVER, it would be good to architect whatever solution is created, to be able to accommodate that in the future.
Something like
llevent.TouchStart(touchHandler,{uuid=ll.GetOwner()})
or
llevent.Listen(listenHandler, {channel=0,message="test",owner=uuid})
Owner is not a filter for listens in the current system, but is one of THE MOST common things done with a listen event.
And most events don't support any sort of filtering at all
H
Harold Linden
WolfGang Senizen
>As for an opaque value, that depends on how much data gets assigned to the handle table, we are precious about our bytes, and what the handle "Object" can do going forward, for instance...
The entry in the handle table is basically "free" in that it doesn't count against your allocation limit, you only have to eat the cost of the single
userdata
per-subscription, maybe 24 or so bytes given TValue
overhead. We have separate memory accounting for things we allocate internally on your behalf vs things that we think you should have to care about, internal tables for state tracking are one of those things.>The ability to filter events better, has been asked for FOREVER, it would be good to architect whatever solution is created, to be able to accommodate that in the future. [...]
The general idea seems reasonable to me (i.e. subscribing to specific channels and the handle object automatically doing the
ll.ListenRemove()
for you.)I don't know how practical filtering on owner is, 'cause in practice you shouldn't rely on
ll.GetOwner()
and friends not changing out from under you, and that's what most people would pass in. I see it as mostly analogous to onmessage
in the browser APIs where you're meant to do origin checks in your own code, and the browser doesn't do that for you. That's less annoying now since you can just have a utility function that decorates a function like owneronly(some_listen_handler)
, unlike with LSL where you didn't have first-class functions.H
Harold Linden
We'll play around a little with some of these ideas and make some pure-SLua wrappers around the existing functionality to see how the API feels in practice. We'll let y'all know once we have something there :)
WolfGang Senizen
Harold Linden
TBH the filtering for listen was probably a poor example.
Its more desired on events that cant be filtered at the moment at all
- Receiving only touches from "current player" of a game
- Filtering which linkset_data values a script receives messages about
- Not receiving every changed event, (i only care about link owner, and region most of the time, but the event is raised every time colour changes too)
- Declaring a filter similar to listen for link messages
All this work is done constantly, an i feel if events could be filtered it'd be not just nice for scripters, but performance too as less scripts will have events queued and have to execute just for things they don't care about. We we can "fake it" in lua now quite easily, but if there was a built in solution would be great.
In web you can subscribe to events from specific elements, make custom events, and while its considered a bit "archaic" jQuery allowed you to subscribe to events and declare a filter before you main event code.
---------
As for multiple event subscribers, one big reason is "helper modules".
I've written myself a library that wraps certain types of events into a promise like system, but then you CANNOT use those events at all anymore except through the promise system as hooking them elsewhere would break it, which is quite... "spooky action at a distance" of it.
H
Harold Linden
WolfGang Senizen Oh yeah, we absolutely want multiple event subscribers, it's pretty much a prerequisite for doing modularization effectively, as well as
await
-like semantics (which we would like to support for ll.HTTPRequest()
and others.As far as declaring filters when registering the event, things get a little weird. We're still sitting on top of the legacy LSL event system, which has pretty inconsistent event filtering, and it's difficult to bolt new filters on top of. Any additional filters we'd add in this API would technically be in user-space (i.e. the script would have to be woken up to check if the event passed our filter) so there's no performance benefit, it's just a question of whether certain new filters should be declarative or procedural.
There are definitely cases where a declarative filter is desirable, like common cases where you only care about an with a specific
id
, we're just trying to work out what those cases are, which we'd need to support out of the gate, and which we can punt on 'til later. Basically, "what is the basic thing we can provide right now
that doesn't totally suck and everyone can agree upon, to be built on later".WolfGang Senizen
Harold Linden Has LL had any thoughts on priority, for multiple event subscribers?
At very list executing them in a specific and documented order would be good. Like first come first serve.
For instance allot of scripts rely on specific linkset structures, and if those structures change, they need to rediscover them, would be annoying if you couldn't control whether that event rand before or after some other changed event that then tried to use those possibly incorrect link numbers.
H
Harold Linden
WolfGang Senizen Yep, they'll be run in subscription order.
We're sensitive to the whole
change & CHANGED_LINK
situation, and eventually there'll be helpers to make that less of a pain as well, like quick helper functions to get a link by name that won't require looping through every link in user code.H
Harold Linden
Also, to be clear, the
function LLEvents.touch_start(num)...
suggestion is just a potential shorthand for registering an event we could support for people who prefer that style. We can have getters and setters handle different values.Jamesp1989 Resident
My vote is on the
local handle = llevent.onTouchStart(function(touches:number) end)
local handle = llevent.dispose(handle)
Layout because it mostly kinda follows how listeners work right now and i like that 🤭 and yea it will allow extra listen functionality on top
nya Resident
I know there's a lot of ways they could approach this, gave feedback in the comments a bit ago -- but event emitter style API would be nice too, i.e.
llevent.on('touch_start', handler)
, llevent.one('touch_start', handler)
, llevent.off('touch_start', handler)