Skip to content
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

[Godot4] return GDScriptFunctionState in someway #3469

Closed
CsloudX opened this issue Oct 26, 2021 · 11 comments
Closed

[Godot4] return GDScriptFunctionState in someway #3469

CsloudX opened this issue Oct 26, 2021 · 11 comments

Comments

@CsloudX
Copy link

CsloudX commented Oct 26, 2021

Describe the project you are working on

NO-GAME APP

Describe the problem or limitation you are having in your project

I want implement this:

  1. a.f1(),b.f2(),c.f3() run sync
  2. wait a.f1(),b.f2(),c.f3() all done,run d.f4()

In Godot3.X, I can code like this:


signal all_done()

var func_states[]


func test():
	append_func(a.f1())
	append_func(b.f2())
	append_func(c.f3())
	if not func_states.empty():
		yield(self,"all_done")
	d.f4()
	

func append_func(func_state):
	if func_state is GDScriptFunctionState:
		func_states.append(func_state)
		func_state.connect("completed",self,"on_func_state_completed",[func_state])


func on_func_state_completed(func_state):
	func_states.erase(func_state)
	if func_states.empty():
		emit_signal("all_done")

I like this codes, But in Godot4, I Can't get GDScriptsState so I can't implement it!

Describe the feature / enhancement and how it helps to overcome the problem or limitation

If not use await keyword, try return GDScriptsFunctionState

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

If implement like that, I can code like this:


signal all_done()

var func_states[]


func test():
	append_func(a.f1())
	append_func(b.f2())
	append_func(c.f3())
	if not func_states.empty():
		await self.all_done
	d.f4()
	

func append_func(func_state):
	if func_state is GDScriptState:
		func_states.append(func_state)
		func_state.completed.connect(on_func_state_completed,[func_state])


func on_func_state_completed(func_state):
	func_states.erase(func_state)
	if func_states.empty():
		emit_signal("all_done")

If this enhancement will not be used often, can it be worked around with a few lines of script?

In GODOT4, I can't implement this

Is there a reason why this should be core and not an add-on in the asset library?

It's GDScript syntax, so can't a add-on

@SilencedPerson
Copy link

I am sorry but there seems to be no GDScriptState class, do you mean GDScriptFunctionState?

@Calinou
Copy link
Member

Calinou commented Oct 26, 2021

@CsloudX Please fill in all the questions in the issue template instead of just leaving a / there. All the questions are required 🙂

@KoBeWi
Copy link
Member

KoBeWi commented Oct 26, 2021

Pretty sure it's still possible or at least there is a workaround for that. If you share some minimal 3.x project with example of your use-case I could try porting it to 4.0.

@dalexeev
Copy link
Member

dalexeev commented Oct 26, 2021

I think that an async function called without await should return a signal of its completion (await waits for the signal passed to it, as vnen described it here). But it doesn't work now (recent changes didn't add this, see godotengine/godot#48830 and godotengine/godot#53822).

This way you should be able to do something like:

Details
class Awaiter:
    signal all_emitted()
    
    var _signals: Array[Signal]
    var _completed := false
    
    func _init(signals: Array[Signal]):
        _signals = signals
        for s in signals:
            s.connect(_on_signal_emitted.bind(s))
    
    func _on_signal_emitted(s: Signal) -> void:
        _signals.erase(s)
        if _signals.is_empty() and not _completed:
            all_emitted.emit()
            _completed = true

func _ready() -> void:
    await Awaiter.new([f(), g(), h()]).all_emitted
    print("All functions completed!")

@KoBeWi
Copy link
Member

KoBeWi commented Oct 26, 2021

So after reading godotengine/godot#53822 (comment) I think there is some misunderstanding how coroutines work in 4.0.

Basically, any function will now always return its intended value (int, String, Object, whatever), whether it's a coroutine or not. If you call a function and try to use its return value and the function is a coroutine, you need to wait before it finishes to be able to obtain the final result. It's very logical and it's the reason why function state is no longer a thing. Coroutines just don't return midway, they actually pause execution.

So implementing this proposal is impossible, because it's against how the new GDScript works. But as I said, there are workarounds. You can create your own object that will await the result and emit a signal.

@dalexeev
Copy link
Member

dalexeev commented Oct 26, 2021

I thought coroutines and non-coroutines would work the same here. In the sense that the value to the right of await is evaluted first. And then coroutines return a completion signal, just like Promise in JavaScript, and regular functions return different values. But either I initially got it wrong, or something changed in the process.

But as I said, there are workarounds. You can create your own object that will await the result and emit a signal.

class AwaitAllCaller:
    signal all_completed()
    
    var _funcs: Array[Callable]
    var _completed := false
    
    func _init(funcs: Array[Callable]):
        _funcs = funcs
        for f in funcs:
            _call_func(f)
    
    func _call_func(f: Callable) -> void:
        await f.call()
        _funcs.erase(f) # For some reason, it doesn't work.
        if _funcs.is_empty() and not _completed:
            all_completed.emit()
            _completed = true

func _ready() -> void:
    await AwaitAllCaller.new([f1, f2, f3.bind("test")]).all_completed
    print("All functions completed!")

@KoBeWi
Copy link
Member

KoBeWi commented Oct 26, 2021

I think that comment is a bit misleading. await method() is a special syntax for waiting for a call. Maaaybe internally it returns a signal and trying to store it is blocked only by the parser/analyzer, but the design is that you can assign only the actual return value, not a function state.

Although maybe I'm wrong, I didn't delve into the new GDScript that much.

@CsloudX
Copy link
Author

CsloudX commented Oct 27, 2021

I am sorry but there seems to be no GDScriptState class, do you mean GDScriptFunctionState?

I'm sorry, YES, I mean GDScriptFunctionState

@CsloudX
Copy link
Author

CsloudX commented Oct 27, 2021

@CsloudX Please fill in all the questions in the issue template instead of just leaving a / there. All the questions are required slightly_smiling_face

OK, thanks

@CsloudX CsloudX changed the title [Godot4] return GDScriptState in someway [Godot4] return GDScriptFunctionState in someway Oct 27, 2021
@CsloudX
Copy link
Author

CsloudX commented Oct 27, 2021

Thanks for @dalexeev , I agree with that, and can I close the comment?

@CsloudX
Copy link
Author

CsloudX commented Oct 27, 2021

I thought coroutines and non-coroutines would work the same here. In the sense that the value to the right of await is evaluted first. And then coroutines return a completion signal, just like Promise in JavaScript, and regular functions return different values. But either I initially got it wrong, or something changed in the process.

But as I said, there are workarounds. You can create your own object that will await the result and emit a signal.

class AwaitAllCaller:
    signal all_completed()
    
    var _funcs: Array[Callable]
    var _completed := false
    
    func _init(funcs: Array[Callable]):
        _funcs = funcs
        for f in funcs:
            _call_func(f)
    
    func _call_func(f: Callable) -> void:
        await f.call()
        _funcs.erase(f) # For some reason, it doesn't work.
        if _funcs.is_empty() and not _completed:
            all_completed.emit()
            _completed = true

func _ready() -> void:
    await AwaitAllCaller.new([f1, f2, f3.bind("test")]).all_completed
    print("All functions completed!")

YES, I like this implement too, but need fix theis earse bug first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants