1- package api
1+ package spx
22
33import (
4+ "sync/atomic"
5+ "time"
6+
47 "github.com/goplus/spx/v2/internal/engine"
58)
69
710// HasInit checks if the SPX engine has been initialized.
8- func HasInit () bool {
9- return engine .GetGame () != nil
11+ func IsSpxEnv () bool {
12+ return engine .IsSpxEnv ()
13+ }
14+
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 ()) {
19+ done := & atomic.Bool {}
20+ // Run the actual logic in a go routine to avoid blocking
21+ go func () {
22+ defer done .Store (true )
23+ fn ()
24+ }()
25+ // Wait for completion while yielding control to SPX
26+ for ! done .Load () {
27+ WaitNextFrame ()
28+ }
29+ }
30+
31+ // Executes the given function in an SPX coroutine from the current Go goroutine context and waits for completion.
32+ // This function blocks until fn finishes execution.
33+ // Use this when you need to synchronously wait for the SPX coroutine to complete.
34+ func RunSpxFromGo (fn func ()) {
35+ done := make (chan struct {}, 1 )
36+ StartSpxCoro (func () {
37+ defer close (done )
38+ fn ()
39+ })
40+ <- done
1041}
1142
12- // Go starts a new spx coroutine that executes the given function concurrently.
43+ // Starts a new spx coroutine that executes the given function concurrently.
1344// This is useful for running multiple operations in parallel without blocking
1445// the main execution flow.
1546//
@@ -25,24 +56,28 @@ func HasInit() bool {
2556//
2657// done := false
2758// // ... do something
28- // api.Go (func() {
59+ // spx.GoAsync (func() {
2960// // ... do something
3061// for !done {
3162// // Do some work here
32- // api .WaitNextFrame() // CRITICAL: Yield control to prevent freezing
63+ // spx .WaitNextFrame() // CRITICAL: Yield control to prevent freezing
3364// }
3465// })
3566//
3667// Example of simple delayed execution:
3768//
38- // api.Go (func() {
39- // api .Wait(2.0)
69+ // spx.GoAsync (func() {
70+ // spx .Wait(2.0)
4071// fmt.Println("Hello after 2 seconds")
4172// })
42- func Go (fn func ()) {
43- engine .Go (engine .GetGame (), func () {
44- fn ()
45- })
73+ func StartSpxCoro (fn func ()) {
74+ if IsSpxEnv () {
75+ engine .Go (engine .GetGame (), func () {
76+ fn ()
77+ })
78+ } else {
79+ go fn ()
80+ }
4681}
4782
4883// Wait pauses the current coroutine for the specified number of seconds.
@@ -62,9 +97,16 @@ func Go(fn func()) {
6297//
6398// Example:
6499//
65- // actualTime := api .Wait(1.5) // Wait for 1.5 seconds
100+ // actualTime := spx .Wait(1.5) // Wait for 1.5 seconds
66101func Wait (secs float64 ) float64 {
67- return engine .Wait (secs )
102+ if IsSpxEnv () {
103+ return engine .Wait (secs )
104+ } else {
105+ // Fallback to a regular wait
106+ startTime := time .Now ()
107+ time .Sleep (time .Duration (secs * float64 (time .Second )))
108+ return time .Since (startTime ).Seconds ()
109+ }
68110}
69111
70112// WaitNextFrame pauses the current coroutine until the next frame is rendered.
@@ -83,9 +125,16 @@ func Wait(secs float64) float64 {
83125// for i := 0; i < 1000; i++ {
84126// // Do some expensive work
85127// if i%100 == 0 {
86- // api .WaitNextFrame() // Yield control every 100 iterations
128+ // spx .WaitNextFrame() // Yield control every 100 iterations
87129// }
88130// }
89131func WaitNextFrame () float64 {
90- return engine .WaitNextFrame ()
132+ if IsSpxEnv () {
133+ return engine .WaitNextFrame ()
134+ } else {
135+ // Fallback to a regular wait
136+ startTime := time .Now ()
137+ time .Sleep (time .Millisecond * 16 ) // Approx 60 FPS
138+ return time .Since (startTime ).Seconds ()
139+ }
91140}
0 commit comments