make repl an engine feature

This commit is contained in:
Josh Deprez 2021-08-26 10:03:11 +10:00
parent 056f0cf5d5
commit 4a4c8669df
3 changed files with 102 additions and 97 deletions

98
engine/repl.go Normal file
View file

@ -0,0 +1,98 @@
package engine
import (
"bufio"
"fmt"
"io"
"io/fs"
"os"
"strings"
)
func (g *Game) REPL(src io.Reader, dst io.Writer, assets fs.FS) error {
sc := bufio.NewScanner(src)
for sc.Scan() {
tok := strings.Split(sc.Text(), " ")
if len(tok) == 0 {
continue
}
switch tok[0] {
case "quit":
os.Exit(0)
case "pause":
g.Disable()
case "resume", "unpause":
g.Enable()
case "save":
if len(tok) != 2 {
fmt.Fprintln(dst, "Usage: save ID")
break
}
id := tok[1]
c := g.Component(id)
if c == nil {
fmt.Fprintf(dst, "Component %q not found\n", id)
break
}
s, ok := c.(Saver)
if !ok {
fmt.Fprintf(dst, "Component %q not a Saver (type %T)\n", id, c)
break
}
if err := s.Save(); err != nil {
fmt.Fprintf(dst, "Couldn't save %q: %v\n", id, err)
}
case "reload":
g.Disable()
g.Hide()
if err := g.Load(assets); err != nil {
fmt.Fprintf(dst, "Couldn't load: %v\n", err)
break
}
g.Prepare()
g.Enable()
g.Show()
case "tree":
var c interface{} = g
if len(tok) == 2 {
// subtree
id := tok[1]
c = g.Component(id)
if c == nil {
fmt.Fprintf(dst, "Component %q not found\n", id)
break
}
}
type item struct {
c interface{}
depth int
}
stack := []item{{c, 0}}
for len(stack) > 0 {
tail := len(stack) - 1
x := stack[tail]
stack = stack[:tail]
c := x.c
indent := ""
if x.depth > 0 {
indent = strings.Repeat(" ", x.depth-1) + "↳ "
}
i, ok := c.(Identifier)
if ok {
fmt.Fprintf(dst, "%s%T %s\n", indent, c, i.Ident())
} else {
fmt.Fprintf(dst, "%s%T\n", indent, c)
}
if s, ok := c.(Scanner); ok {
for _, y := range s.Scan() {
stack = append(stack, item{y, x.depth + 1})
}
}
}
}
}
return sc.Err()
}

Binary file not shown.

99
main.go
View file

@ -1,12 +1,10 @@
package main
import (
"bufio"
_ "image/png"
"log"
"os"
"runtime"
"strings"
"drjosh.dev/gurgle/engine"
"drjosh.dev/gurgle/game"
@ -38,104 +36,13 @@ func main() {
}
g.Prepare()
if runtime.GOOS != "js" {
// Run a repl on the console.
go repl(g)
go g.REPL(os.Stdin, os.Stdout, game.Assets)
}
// ... while the game also runs
if err := ebiten.RunGame(g); err != nil {
log.Fatalf("Game error: %v", err)
}
}
func repl(g *engine.Game) {
if runtime.GOOS == "js" {
// can't os.Stdin on js
return
}
sc := bufio.NewScanner(os.Stdin)
for sc.Scan() {
tok := strings.Split(sc.Text(), " ")
if len(tok) == 0 {
continue
}
switch tok[0] {
case "quit":
os.Exit(0)
case "pause":
g.Disable()
case "resume", "unpause":
g.Enable()
case "save":
if len(tok) != 2 {
log.Print("Usage: save ID")
break
}
id := tok[1]
c := g.Component(id)
if c == nil {
log.Printf("Component %q not found", id)
break
}
s, ok := c.(engine.Saver)
if !ok {
log.Printf("Component %q not a Saver (type %T)", id, c)
break
}
if err := s.Save(); err != nil {
log.Printf("Couldn't save %q: %v", id, err)
}
case "reload":
g.Disable()
g.Hide()
if err := g.Load(game.Assets); err != nil {
log.Printf("Couldn't load: %v", err)
break
}
g.Prepare()
g.Enable()
g.Show()
case "tree":
var c interface{} = g
if len(tok) == 2 {
// subtree
id := tok[1]
c = g.Component(id)
if c == nil {
log.Printf("Component %q not found", id)
break
}
}
type item struct {
c interface{}
depth int
}
stack := []item{{c, 0}}
for len(stack) > 0 {
tail := len(stack) - 1
x := stack[tail]
stack = stack[:tail]
c := x.c
indent := ""
if x.depth > 0 {
indent = strings.Repeat(" ", x.depth-1) + "↳ "
}
i, ok := c.(engine.Identifier)
if ok {
log.Printf("%s%T %s", indent, c, i.Ident())
} else {
log.Printf("%s%T", indent, c)
}
if s, ok := c.(engine.Scanner); ok {
for _, y := range s.Scan() {
stack = append(stack, item{y, x.depth + 1})
}
}
}
}
}
if err := sc.Err(); err != nil {
log.Fatalf("Couldn't scan stdin: %v", err)
}
}