125 lines
2.8 KiB
Go
125 lines
2.8 KiB
Go
//go:build js
|
|
|
|
package player
|
|
|
|
import (
|
|
"errors"
|
|
"path"
|
|
"strconv"
|
|
"syscall/js"
|
|
|
|
"aego/platform"
|
|
"aego/sdk"
|
|
)
|
|
|
|
const (
|
|
dataGlobal = "aegoData"
|
|
hookGlobal = "aegoGame"
|
|
failGlobal = "aegoFail"
|
|
startedGlobal = "aegoStarted"
|
|
)
|
|
|
|
func Main(mods ...sdk.Module) {
|
|
g := js.Global()
|
|
b, err := takeBundle(g.Get(dataGlobal))
|
|
g.Set(dataGlobal, js.Undefined())
|
|
if err != nil {
|
|
fail(err.Error())
|
|
return
|
|
}
|
|
q := g.Get("URLSearchParams").New(g.Get("location").Get("search"))
|
|
param := func(k string) string {
|
|
if v := q.Call("get", k); v.Type() == js.TypeString {
|
|
return v.String()
|
|
}
|
|
return ""
|
|
}
|
|
seed, _ := strconv.ParseUint(param("seed"), 10, 64)
|
|
cfg := Config{Bundle: b, Profile: param("profile"), Scene: param("scene"), Debug: param("debug") != "", Seed: seed}
|
|
p, err := Open(cfg, sdk.AdaptAll(mods)...)
|
|
if err != nil {
|
|
fail(err.Error())
|
|
return
|
|
}
|
|
expose(p)
|
|
if f := g.Get(startedGlobal); f.Type() == js.TypeFunction {
|
|
f.Invoke()
|
|
}
|
|
if err := p.Run(); err != nil {
|
|
fail(err.Error())
|
|
}
|
|
}
|
|
|
|
func fail(msg string) {
|
|
if f := js.Global().Get(failGlobal); f.Type() == js.TypeFunction {
|
|
f.Invoke(msg)
|
|
return
|
|
}
|
|
js.Global().Get("console").Call("error", "game: "+msg)
|
|
}
|
|
|
|
func bytesOf(u js.Value) []byte {
|
|
if u.Type() != js.TypeObject {
|
|
return nil
|
|
}
|
|
out := make([]byte, u.Get("byteLength").Int())
|
|
js.CopyBytesToGo(out, u)
|
|
return out
|
|
}
|
|
|
|
func takeBundle(v js.Value) (*Bundle, error) {
|
|
if v.Type() != js.TypeObject {
|
|
return nil, errors.New("the page did not load the game data")
|
|
}
|
|
b := &Bundle{Manifest: bytesOf(v.Get("manifest")), Files: map[string][]byte{}}
|
|
files := v.Get("files")
|
|
keys := js.Global().Get("Object").Call("keys", files)
|
|
for i := 0; i < keys.Length(); i++ {
|
|
k := keys.Index(i).String()
|
|
b.Files[k] = bytesOf(files.Get(k))
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
func (p *Player) userPath(name string) (string, bool) {
|
|
if p.profileDir == "" {
|
|
return "", false
|
|
}
|
|
return path.Join(p.profileDir, path.Clean("/"+name)), true
|
|
}
|
|
|
|
func expose(p *Player) {
|
|
api := js.Global().Get("Object").New()
|
|
api.Set("frames", js.FuncOf(func(js.Value, []js.Value) any {
|
|
return float64(p.Engine.MainWorld().Time.Frame())
|
|
}))
|
|
api.Set("userRead", js.FuncOf(func(_ js.Value, a []js.Value) any {
|
|
if len(a) == 0 {
|
|
return js.Null()
|
|
}
|
|
at, ok := p.userPath(a[0].String())
|
|
if !ok {
|
|
return js.Null()
|
|
}
|
|
data, err := platform.UserFiles().ReadFile(at)
|
|
if err != nil {
|
|
return js.Null()
|
|
}
|
|
return string(data)
|
|
}))
|
|
api.Set("userWrite", js.FuncOf(func(_ js.Value, a []js.Value) any {
|
|
if len(a) < 2 {
|
|
return "userWrite needs a name and a text"
|
|
}
|
|
at, ok := p.userPath(a[0].String())
|
|
if !ok {
|
|
return "there is no player profile"
|
|
}
|
|
if err := platform.UserFiles().WriteFile(at, []byte(a[1].String())); err != nil {
|
|
return err.Error()
|
|
}
|
|
return js.Null()
|
|
}))
|
|
js.Global().Set(hookGlobal, api)
|
|
}
|