-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Yield on signal implementation? #80
Comments
Looking at the way ECMAScript is doing it, they modified the godot.Object.connect function to allow you to pass in anonymous functions instead of strings that reference the object's instance methods. So what I was thinking was let's make another connect where, instead of passing a string that references the instance method to be called, you pass a proc that handles the signal. That may require macros or templates, but the benefit is we can do something like this instead: method ready() =
self.connect("signal", self) do (data: string) -> void:
# do stuff here If we figure this out, then what we can do is create Future[T]s using asyncdispatch/asyncfutures in a callback function and attach that the signal just in ECMAScript. What do you think? |
@geekrelief I opened the thread. We can discuss things here.
I'm gonna revisit the signal macro code, although I think your proposal is fairly sound. On top of that, I think adding an anonymous proc is potentially doable because iirc all it requires is for us to register anonymous procs. The reason I mentioned the anonymous procs for |
Yes, one way or another we need to register the callback proc with godot. Actually, I have some proof of concept code. But I need to implement things on the macro side. import godot
import godotapi / [timer]
import hot
import asyncdispatch
gdobj TimerComp of Timer:
method ready() =
registerFrameCallback(
proc() =
if hasPendingOperations():
poll(0)
)
asyncCheck self.fireTimer()
proc fire_timer() {.async.} =
self.one_shot = true
self.start(1)
await self.onSignal(self, "timeout")
print "timeout 1"
self.one_shot = true
self.start(1)
await self.onSignal(self, "timeout")
print "timeout 2"
var f:Future[void]
proc callback() {.gdExport.} =
self.f.complete
proc onSignal(target:Object, signalName:string):Future[void] =
self.f = newFuture[void]("onSignal")
discard target.connect(signalName, self, "callback", flags = CONNECT_ONESHOT)
self.f Somewhere in the gdobj macro we'll check for a proc with I'm aiming for something like this. proc fire_timer() {.async.} =
self.one_shot = true
self.start(1)
onSignal(self.timer, "timeout")
print "timeout"
onSignal(self.area2d, "area_entered", (area:Area2D, shape_idx:int, ...))
print &"{area=}"
onSignal(self.area2d, "area_entered", (area2:Area2D, shape_idx2:int, ...))
print &"{area2=} So you can call I need to work on some other stuff at the moment, but hopefully I can start on this later tonight or tomorrow. |
No problem, I've been splitting my time among different projects and so that's why I haven't been as attentive to this as I want to be at the moment. |
I got this working on gdnim https://github.com/geekrelief/gdnim/blob/master/components/sprite_comp.nim. |
Updated my PR for signals with async handling. #79 |
Nice, I've been setting up my Linux (NixOS) environment on my desktop, and I'm still trying to get Godot to compile. |
@endragor left a comment on my PR, so there might be some changes coming up with regards to implementation or api. It's pretty much my first draft, but you can try it out to see if its "ergonomical" or anything obviously broken. |
Separated the PR for async handling. #83 |
Based on my conversation with endragor. I don't think this will ever make it into godot-nim unless there's an async/await rewrite to remove closure cycles. My undertanding is still a bit murky on this, but because of the way async generates a callback closing over Future, it'll force the GC to run which he doesn't want. So it's up to the end users to decide if they want to implement / incorporate this. This article on ARC / ORC mentions it in the ORC section, so this might never get fixed. |
Here's more info https://news.ycombinator.com/item?id=24800161
|
I fixed the issue I was having with ORC and gdnim. So I've made it the default gc so we can collect those async cycles. |
@geekrelief So if I'm understanding this correctly, the solution works because ORC is designed to handle async? |
So what's your strategy for doing this? To be honest I looked at this a while ago, and didn't understand nim's async and godot-nim well enough at the time to move forward.
Here are my thoughts on this so far. I can see how we'd used
asynchdispatch
'spoll
in themethod process
to process Futures. We'd explicitly have to add something like:So Futures will complete.
Then we'd tag a function with
{.async.}
, and since that function returns a Future when invoked. Inside that function we would then useawait
on some function, call itonSignal
, that returns a Future used in a callback forconnect
.What I'm trying to figure out is how to create/reference the callback to be passed to
connect
when it only accepts a string for the callback? I don't think we can automatically generate the callback inside ofonSignal
without passing the signal parameter types since a signal api is not part of gdnative. Take for example, Area2D has aarea_shape_entered ( int area_id, Area2D area, int area_shape, int self_shape )
signal, we'd need to pass the signal parameters explicitly, or modify the gdnative api to include signals.So assuming we have all the info we need to generate a callback what are the options for passing it to
connect
? Currently callbacks are class member procs that need{.gdExport.}
, which usesnativeScriptRegisterMethod
to register it with godot. But we can't use{.gdExport.}
on a callback inside of a proc. SoonSignal
needs a new macro to process it, call itgenSignalCallback
, that willgenSym
a name for the callback, register it with godot, then pass the callback name toconnect
. But the problem with that isgenSignalCallback
needs to write to the AST generated bygodotmacros gdobj
. I don't know if that's even possible "out-of-band", so we'd have to parse procs whengdobj
is generating the AST to add the generated callback as another method to be registered. Since only{.async.}
functions can containawait onSignal
we'd only have to parse those functions.Hmm so this seems doable. Thoughts?
Originally posted by @geekrelief in #74 (comment)
The text was updated successfully, but these errors were encountered: