@@ -7,20 +7,63 @@ import (
77 "github.com/goplus/spx/v2/internal/engine"
88)
99
10- // HasInit checks if the SPX engine has been initialized.
11- func IsSpxEnv () bool {
12- return engine .IsSpxEnv ()
10+ func isSpxEnv () bool {
11+ return engine .GetGame () != nil
1312}
1413
15- // Executes the given function in a native Go goroutine from the current SPX coroutine context and waits for completion.
16- // While waiting, it yields control via waitNextFrame to avoid blocking the SPX main thread.
17- // Use this when you need to run potentially blocking Go operations (e.g., network requests, file I/O) from within SPX.
18- func RunGoFromSpx (fn func ()) {
14+ // IsInCoroutine checks whether the current execution context is within an SPX coroutine.
15+ // Returns true if running inside an SPX coroutine, false if running in a regular Go goroutine
16+ // or the main thread.
17+ //
18+ // This function is useful for determining the appropriate execution strategy when your code
19+ // needs to work in both SPX coroutine and regular Go contexts.
20+ //
21+ // Example:
22+ //
23+ // if spx.IsInCoroutine() {
24+ // // Use SPX-specific functions like Wait() or WaitNextFrame()
25+ // spx.Wait(1.0)
26+ // } else {
27+ // // Use regular Go functions
28+ // time.Sleep(time.Second)
29+ // }
30+ func IsInCoroutine () bool {
31+ return engine .IsInCoroutine ()
32+ }
33+
34+ // ExecuteNative executes the given function in a native Go goroutine and waits for its completion.
35+ // While waiting, if it is in spx corotine, it yields control via WaitNextFrame to avoid blocking
36+ // the SPX main thread.
37+ //
38+ // This function is essential when you need to perform blocking Go operations (such as network requests,
39+ // file I/O, or system calls) from within an SPX coroutine without freezing the game engine.
40+ //
41+ // If called from outside an SPX coroutine context, the function executes synchronously.
42+ //
43+ // Example:
44+ //
45+ // spx.ExecuteNative(func() {
46+ // // Perform blocking network request
47+ // resp, err := http.Get("https://api.example.com/data")
48+ // if err != nil {
49+ // log.Printf("Error: %v", err)
50+ // return
51+ // }
52+ // defer resp.Body.Close()
53+ // // Process response...
54+ // })
55+ func ExecuteNative (fn func (owner any )) {
56+ // if not in spx coro, just run it
57+ if ! engine .IsInCoroutine () {
58+ fn (nil )
59+ return
60+ }
61+ owner := engine .GetCoroutineOwner ()
1962 done := & atomic.Bool {}
20- // Run the actual logic in a go routine to avoid blocking
63+ // Execute the actual logic in a go routine to avoid blocking
2164 go func () {
2265 defer done .Store (true )
23- fn ()
66+ fn (owner )
2467 }()
2568 // Wait for completion while yielding control to SPX
2669 for ! done .Load () {
@@ -31,11 +74,22 @@ func RunGoFromSpx(fn func()) {
3174// Executes the given function in an SPX coroutine from the current Go goroutine context and waits for completion.
3275// This function blocks until fn finishes execution.
3376// Use this when you need to synchronously wait for the SPX coroutine to complete.
34- func RunSpxFromGo (fn func ()) {
77+ //
78+ // Parameters:
79+ //
80+ // owner - The SPX coroutine owner. When the owner is destroyed, all coroutines created by this owner will be properly stopped.
81+ // fn - The function to execute in the coroutine context.
82+ func Execute (owner any , fn func (owner any )) {
83+ // in spx coro, just run it
84+ if engine .IsInCoroutine () {
85+ fn (owner )
86+ return
87+ }
88+
3589 done := make (chan struct {}, 1 )
36- StartSpxCoro ( func () {
90+ Go ( owner , func (any ) {
3791 defer close (done )
38- fn ()
92+ fn (owner )
3993 })
4094 <- done
4195}
@@ -44,6 +98,12 @@ func RunSpxFromGo(fn func()) {
4498// This is useful for running multiple operations in parallel without blocking
4599// the main execution flow.
46100//
101+ // Parameters:
102+ //
103+ // owner - The SPX coroutine owner. When the owner is destroyed, all coroutines created by this owner will be properly stopped.
104+ // If nil, the current coroutine's owner or the game instance will be used as the owner.
105+ // fn - The function to execute in the coroutine context.
106+ //
47107// IMPORTANT: For long-running tasks, you MUST call Wait() or WaitNextFrame()
48108// periodically to yield control back to the engine. Without these calls,
49109// the main thread will wait indefinitely for the coroutine to complete,
@@ -56,7 +116,7 @@ func RunSpxFromGo(fn func()) {
56116//
57117// done := false
58118// // ... do something
59- // spx.GoAsync( func() {
119+ // spx.Go(owner, func(owner any ) {
60120// // ... do something
61121// for !done {
62122// // Do some work here
@@ -66,17 +126,24 @@ func RunSpxFromGo(fn func()) {
66126//
67127// Example of simple delayed execution:
68128//
69- // spx.GoAsync( func() {
129+ // spx.Go(owner, func(owner any ) {
70130// spx.Wait(2.0)
71131// fmt.Println("Hello after 2 seconds")
72132// })
73- func StartSpxCoro (fn func ()) {
74- if IsSpxEnv () {
75- engine .Go (engine .GetGame (), func () {
76- fn ()
133+ func Go (owner any , fn func (owner any )) {
134+ if isSpxEnv () {
135+ if owner == nil {
136+ if IsInCoroutine () {
137+ owner = engine .GetCoroutineOwner ()
138+ } else {
139+ owner = engine .GetGame ()
140+ }
141+ }
142+ engine .Go (owner , func () {
143+ fn (owner )
77144 })
78145 } else {
79- go fn ()
146+ go fn (owner )
80147 }
81148}
82149
@@ -99,7 +166,7 @@ func StartSpxCoro(fn func()) {
99166//
100167// actualTime := spx.Wait(1.5) // Wait for 1.5 seconds
101168func Wait (secs float64 ) float64 {
102- if IsSpxEnv () {
169+ if engine . IsInCoroutine () {
103170 return engine .Wait (secs )
104171 } else {
105172 // Fallback to a regular wait
@@ -129,7 +196,7 @@ func Wait(secs float64) float64 {
129196// }
130197// }
131198func WaitNextFrame () float64 {
132- if IsSpxEnv () {
199+ if engine . IsInCoroutine () {
133200 return engine .WaitNextFrame ()
134201 } else {
135202 // Fallback to a regular wait
0 commit comments