Compare commits
44
Commits
stage-8
...
v0.9.0-beta.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
914cf39150 | ||
|
|
dede4d8af3 | ||
|
|
b6dd46f523 | ||
|
|
9d32052624 | ||
|
|
e636801578 | ||
|
|
01fdeef7a8 | ||
|
|
71354f6a60 | ||
|
|
8ae6832503 | ||
|
|
183d75ed50 | ||
|
|
19edcbf9b2 | ||
|
|
8ed77a7638 | ||
|
|
ca3c9bf2e7 | ||
|
|
db59f117a4 | ||
|
|
0d2c20da26 | ||
|
|
dd41ff15a5 | ||
|
|
599e09a35d | ||
|
|
60614938be | ||
|
|
a60ca47d31 | ||
|
|
00ae2418a0 | ||
|
|
8fc9b4c287 | ||
|
|
b29934b51d | ||
|
|
c475b42e5d | ||
|
|
b103816400 | ||
|
|
487170dd9c | ||
|
|
57579356d6 | ||
|
|
d5377410e4 | ||
|
|
f6c83a6e0c | ||
|
|
8d7712d671 | ||
|
|
63980faccb | ||
|
|
0041e7e639 | ||
|
|
e334be569f | ||
|
|
b488958444 | ||
|
|
5875a65193 | ||
|
|
6ab66cb8ae | ||
|
|
f8fe54e9c5 | ||
|
|
e4326e7e7c | ||
|
|
6a22396467 | ||
|
|
21e03128a8 | ||
|
|
e5b1964f3e | ||
|
|
cffa66514b | ||
|
|
2744cff481 | ||
|
|
91052a54db | ||
|
|
7f4d87ef2c | ||
|
|
6cc0673082 |
@@ -5,3 +5,4 @@ site
|
||||
exports
|
||||
**/.aego
|
||||
*.test
|
||||
.claude
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,8 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [master]
|
||||
tags: ["v*"]
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
@@ -27,6 +28,7 @@ jobs:
|
||||
- name: system packages
|
||||
run: sudo apt-get update && sudo apt-get install -y $LINUX_PKGS
|
||||
- uses: actions/cache@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: .cache/native
|
||||
key: native-linux_amd64-${{ hashFiles('third_party/sdl/version.txt', 'tools/bootstrap-native.sh') }}
|
||||
@@ -42,9 +44,6 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- { os: ubuntu-24.04, goos: linux, goarch: amd64 }
|
||||
- { os: windows-2022, goos: windows, goarch: amd64 }
|
||||
- { os: macos-15-intel, goos: darwin, goarch: amd64 }
|
||||
- { os: macos-15, goos: darwin, goarch: arm64 }
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
@@ -74,6 +73,7 @@ jobs:
|
||||
if: runner.os == 'macOS'
|
||||
run: brew list ninja >/dev/null 2>&1 || brew install ninja
|
||||
- uses: actions/cache@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: .cache/native
|
||||
key: native-${{ matrix.goos }}_${{ matrix.goarch }}-${{ hashFiles('third_party/sdl/version.txt', 'tools/bootstrap-native.sh') }}
|
||||
@@ -112,4 +112,39 @@ jobs:
|
||||
- name: example project
|
||||
run: |
|
||||
/tmp/aego check examples/native-rotator
|
||||
/tmp/aego run examples/native-rotator --headless --frames 60
|
||||
/tmp/aego run examples/native-rotator --headless --frames 60
|
||||
|
||||
release:
|
||||
needs: [checks, build]
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- name: linux packages
|
||||
run: sudo apt-get update && sudo apt-get install -y $LINUX_PKGS
|
||||
- uses: actions/cache@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: .cache/native
|
||||
key: native-linux_amd64-${{ hashFiles('third_party/sdl/version.txt', 'tools/bootstrap-native.sh') }}
|
||||
- run: bash tools/bootstrap-native.sh
|
||||
- name: windows tools
|
||||
run: sudo apt-get install -y mingw-w64 nsis zip
|
||||
- name: publish
|
||||
env:
|
||||
REQUIRE_WINDOWS: "1"
|
||||
GITEA_RESOLVE: git.akarpov.ru:443:79.142.92.250
|
||||
run: |
|
||||
case "$GITHUB_REF" in
|
||||
refs/tags/v*) sh tools/release/release.sh "$GITHUB_REF_NAME" ;;
|
||||
refs/heads/master) sh tools/release/release.sh nightly ;;
|
||||
esac
|
||||
|
||||
@@ -11,4 +11,10 @@ tools/genicons/genicons
|
||||
/aego
|
||||
/aego-editor
|
||||
/aego-playground
|
||||
/aego-runtime
|
||||
*.syso
|
||||
*.test
|
||||
|
||||
/tests/golden/testdata/actual-*
|
||||
tools/websmoke/node_modules/
|
||||
tools/websmoke/golden/actual-*.png
|
||||
|
||||
+285
-40
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mustFind(t *testing.T, name string) *Command {
|
||||
t.Helper()
|
||||
c, ok := Find(name)
|
||||
if !ok {
|
||||
t.Fatalf("no command %s", name)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func TestParseArgs(t *testing.T) {
|
||||
set := mustFind(t, "scene.set")
|
||||
a, f, err := parseArgs(set, []string{"s.aescene", "--json", "Player", "--dry-run", "Body2D.mass", "-3"})
|
||||
if err != nil || !f.json || a.Str("value") != "-3" || a.Str("node") != "Player" || !a.Bool("dry-run") {
|
||||
t.Fatalf("flags after positionals: %v %+v %v", a, f, err)
|
||||
}
|
||||
add := mustFind(t, "scene.add-node")
|
||||
for _, toks := range [][]string{{"s", "N", "--index=1"}, {"s", "N", "--index", "1"}} {
|
||||
a, _, err := parseArgs(add, toks)
|
||||
if n, _ := a.Int("index", -1); err != nil || n != 1 {
|
||||
t.Fatalf("%v: %v %v", toks, a, err)
|
||||
}
|
||||
}
|
||||
a, _, err = parseArgs(set, []string{"s", "--", "Player", "Body2D.mass", "--odd"})
|
||||
if err != nil || a.Str("value") != "--odd" {
|
||||
t.Fatalf("after --: %v %v", a, err)
|
||||
}
|
||||
if _, _, err := parseArgs(set, []string{"s", "--nope"}); err == nil {
|
||||
t.Fatal("an unknown flag was accepted")
|
||||
}
|
||||
if _, _, err := parseArgs(set, []string{"s", "a", "b", "c", "d"}); err == nil {
|
||||
t.Fatal("an extra positional was accepted")
|
||||
}
|
||||
var out, errw bytes.Buffer
|
||||
if code := Main(nil, []string{"scene", "set", "s", "--nope"}, &out, &errw, Options{}); code != 2 {
|
||||
t.Fatalf("unknown flag exit %d", code)
|
||||
}
|
||||
if code := Main(nil, []string{"scene", "set", "a", "b", "c", "d", "e"}, &out, &errw, Options{}); code != 2 {
|
||||
t.Fatalf("extra positional exit %d", code)
|
||||
}
|
||||
if code := Main(nil, []string{"scene", "help"}, &out, &errw, Options{}); code != 0 {
|
||||
t.Fatalf("help exit %d", code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchemasAndToolNames(t *testing.T) {
|
||||
seen := map[string]bool{}
|
||||
for _, c := range Commands() {
|
||||
if seen[c.Tool()] {
|
||||
t.Fatalf("tool name %s is used twice", c.Tool())
|
||||
}
|
||||
seen[c.Tool()] = true
|
||||
b, err := json.Marshal(c.Schema())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var sch struct {
|
||||
Type string `json:"type"`
|
||||
Properties map[string]map[string]any `json:"properties"`
|
||||
Required []string `json:"required"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &sch); err != nil || sch.Type != "object" {
|
||||
t.Fatalf("%s: %s", c.Name, b)
|
||||
}
|
||||
var want []string
|
||||
for _, x := range c.AllArgs() {
|
||||
if x.Required {
|
||||
want = append(want, x.Name)
|
||||
}
|
||||
if _, ok := sch.Properties[x.Name]; !ok {
|
||||
t.Fatalf("%s: no property %s", c.Name, x.Name)
|
||||
}
|
||||
}
|
||||
if len(want) != len(sch.Required) {
|
||||
t.Fatalf("%s: required %v, want %v", c.Name, sch.Required, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"aego/asset"
|
||||
"aego/project/refs"
|
||||
"aego/scene/mutate"
|
||||
)
|
||||
|
||||
func projectCommands() []*Command {
|
||||
return []*Command{
|
||||
{Name: "project.validate", Doc: "missing assets and nodes, import errors, prefabs and scenes that do not load", Run: (*Session).projectValidate},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) projectValidate(Args) (*Output, error) {
|
||||
out := &Output{}
|
||||
add := func(err bool, node, msg string) {
|
||||
out.Problems = append(out.Problems, mutate.Problem{Error: err, Node: node, Message: msg})
|
||||
}
|
||||
for _, m := range refs.MissingAssets(s.Project, s.Pipeline) {
|
||||
add(true, "", "missing asset "+m.Label())
|
||||
}
|
||||
for _, m := range refs.MissingNodes(s.Pipeline) {
|
||||
add(true, m.Scene+" "+m.Path, m.Owner+"."+m.Field+" references a node that is not in the scene")
|
||||
}
|
||||
if s.scriptErr != nil {
|
||||
add(true, "", "script types: "+s.scriptErr.Error())
|
||||
}
|
||||
for _, e := range s.libErrs {
|
||||
add(true, "", e.Error())
|
||||
}
|
||||
for _, x := range s.Pipeline.Assets() {
|
||||
if x.Error != "" {
|
||||
add(true, x.Path, x.Error)
|
||||
continue
|
||||
}
|
||||
if x.Kind != asset.ArtScene && x.Kind != asset.ArtPrefab {
|
||||
continue
|
||||
}
|
||||
src, err := s.read(x.Path)
|
||||
if err != nil {
|
||||
add(true, x.Path, err.Error())
|
||||
continue
|
||||
}
|
||||
doc, err := mutate.Load(src, x.Path, s.env())
|
||||
if err != nil {
|
||||
add(true, x.Path, err.Error())
|
||||
continue
|
||||
}
|
||||
for _, p := range doc.Problems {
|
||||
add(p.Error, x.Path+" "+p.Node, p.Message)
|
||||
}
|
||||
}
|
||||
errs, warns := 0, 0
|
||||
for _, p := range out.Problems {
|
||||
if p.Error {
|
||||
errs++
|
||||
} else {
|
||||
warns++
|
||||
}
|
||||
}
|
||||
out.Notes = []string{"project: " + strconv.Itoa(errs) + " error(s), " + strconv.Itoa(warns) + " warning(s)"}
|
||||
return out, nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,6 @@ import (
|
||||
"aego/core/log"
|
||||
"aego/engine/base"
|
||||
"aego/engine/module"
|
||||
"aego/platform/input"
|
||||
"aego/scene"
|
||||
"aego/script/bind"
|
||||
)
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
type Options struct {
|
||||
Seed uint64
|
||||
Testing bool
|
||||
Input *input.AxisConfig
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
|
||||
@@ -4,10 +4,21 @@ import (
|
||||
"aego/app/assembly"
|
||||
"aego/engine/base"
|
||||
"aego/engine/module"
|
||||
"aego/modules/accessibility"
|
||||
"aego/modules/audio"
|
||||
"aego/modules/camera"
|
||||
"aego/modules/dataasset"
|
||||
"aego/modules/debug"
|
||||
"aego/modules/display"
|
||||
inputmod "aego/modules/input"
|
||||
"aego/modules/physics"
|
||||
"aego/modules/render2d"
|
||||
"aego/modules/render3d"
|
||||
"aego/modules/savegame"
|
||||
"aego/modules/scripting"
|
||||
settingsmod "aego/modules/settings"
|
||||
"aego/modules/spawn"
|
||||
"aego/modules/tween"
|
||||
"aego/script/stdlib"
|
||||
)
|
||||
|
||||
@@ -15,6 +26,10 @@ func core() assembly.Entry {
|
||||
return assembly.Entry{ID: base.CoreID, New: func(assembly.Options) module.Module { return base.Core{} }}
|
||||
}
|
||||
|
||||
func inputEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: inputmod.ID, New: func(assembly.Options) module.Module { return inputmod.New() }}
|
||||
}
|
||||
|
||||
func render2dEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: render2d.ID, New: func(assembly.Options) module.Module { return render2d.New() }}
|
||||
}
|
||||
@@ -25,7 +40,7 @@ func render3dEntry() assembly.Entry {
|
||||
|
||||
func scriptEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: scripting.ID, New: func(o assembly.Options) module.Module {
|
||||
return scripting.New(stdlib.Options{Seed: o.Seed, Testing: o.Testing, Input: o.Input})
|
||||
return scripting.New(stdlib.Options{Seed: o.Seed, Testing: o.Testing})
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -33,6 +48,47 @@ func physicsEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: physics.ID, New: func(assembly.Options) module.Module { return physics.New() }}
|
||||
}
|
||||
|
||||
func Default() assembly.Catalog {
|
||||
return assembly.Catalog{core(), render2dEntry(), render3dEntry(), scriptEntry(), physicsEntry()}
|
||||
func debugEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: debug.ID, New: func(assembly.Options) module.Module { return debug.New() }}
|
||||
}
|
||||
|
||||
func displayEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: display.ID, New: func(assembly.Options) module.Module { return display.New() }}
|
||||
}
|
||||
|
||||
func audioEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: audio.ID, New: func(assembly.Options) module.Module { return audio.New() }}
|
||||
}
|
||||
|
||||
func accessibilityEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: accessibility.ID, New: func(assembly.Options) module.Module { return accessibility.New() }}
|
||||
}
|
||||
|
||||
func settingsEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: settingsmod.ID, Requires: []string{inputmod.ID}, New: func(assembly.Options) module.Module { return settingsmod.New() }}
|
||||
}
|
||||
|
||||
func tweenEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: tween.ID, Requires: []string{scripting.ID}, New: func(assembly.Options) module.Module { return tween.New() }}
|
||||
}
|
||||
|
||||
func cameraEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: camera.ID, Requires: []string{scripting.ID}, New: func(assembly.Options) module.Module { return camera.New() }}
|
||||
}
|
||||
|
||||
func dataEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: dataasset.ID, New: func(assembly.Options) module.Module { return dataasset.New() }}
|
||||
}
|
||||
|
||||
func spawnEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: spawn.ID, New: func(assembly.Options) module.Module { return spawn.New() }}
|
||||
}
|
||||
|
||||
func savegameEntry() assembly.Entry {
|
||||
return assembly.Entry{ID: savegame.ID, Requires: []string{scripting.ID, spawn.ID}, New: func(assembly.Options) module.Module { return savegame.New() }}
|
||||
}
|
||||
|
||||
func Default() assembly.Catalog {
|
||||
return assembly.Catalog{core(), dataEntry(), inputEntry(), render2dEntry(), render3dEntry(), scriptEntry(), physicsEntry(), debugEntry(),
|
||||
displayEntry(), audioEntry(), accessibilityEntry(), settingsEntry(), tweenEntry(), cameraEntry(), spawnEntry(), savegameEntry()}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ package builtin
|
||||
import "aego/app/assembly"
|
||||
|
||||
func Headless() assembly.Catalog {
|
||||
return assembly.Catalog{core(), scriptEntry(), physicsEntry()}
|
||||
return assembly.Catalog{core(), dataEntry(), inputEntry(), scriptEntry(), physicsEntry(), debugEntry(), displayEntry(), audioEntry(), accessibilityEntry(), settingsEntry(), tweenEntry(), cameraEntry(), spawnEntry(), savegameEntry()}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package builtin
|
||||
|
||||
import "aego/app/assembly"
|
||||
|
||||
func Runtime() assembly.Catalog {
|
||||
return Default()
|
||||
}
|
||||
@@ -24,6 +24,10 @@ func Manifest(user ...module.Module) (*manifest.Manifest, error) {
|
||||
}
|
||||
|
||||
func AuditIcons(user ...module.Module) (*module.IconReport, error) {
|
||||
return AuditIconsWith(nil, user...)
|
||||
}
|
||||
|
||||
func AuditIconsWith(extra []module.IconUse, user ...module.Module) (*module.IconReport, error) {
|
||||
types, err := assembly.NewTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -32,7 +36,7 @@ func AuditIcons(user ...module.Module) (*module.IconReport, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return module.Audit(types, bind.NewManifest(), append(mods, user...)...)
|
||||
return module.AuditUses(extra, types, bind.NewManifest(), append(mods, user...)...)
|
||||
}
|
||||
|
||||
func IconSources() []string {
|
||||
|
||||
@@ -2,6 +2,12 @@ package editors
|
||||
|
||||
import (
|
||||
"aego/app/assembly"
|
||||
"aego/modules/camera"
|
||||
cameraeditor "aego/modules/camera/editor"
|
||||
"aego/modules/debug"
|
||||
debugeditor "aego/modules/debug/editor"
|
||||
inputmod "aego/modules/input"
|
||||
inputeditor "aego/modules/input/editor"
|
||||
"aego/modules/physics"
|
||||
physicseditor "aego/modules/physics/editor"
|
||||
"aego/modules/render2d"
|
||||
@@ -12,7 +18,10 @@ import (
|
||||
|
||||
func With(c assembly.Catalog) assembly.Catalog {
|
||||
return c.
|
||||
With(inputmod.ID, inputeditor.Hook).
|
||||
With(render2d.ID, render2deditor.Hook).
|
||||
With(render3d.ID, render3deditor.Hook).
|
||||
With(physics.ID, physicseditor.Hook)
|
||||
With(physics.ID, physicseditor.Hook).
|
||||
With(camera.ID, cameraeditor.Hook).
|
||||
With(debug.ID, debugeditor.Hook)
|
||||
}
|
||||
|
||||
@@ -44,3 +44,14 @@ func (h hooked) Shutdown() {
|
||||
sd.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func (h hooked) Reconfigure(ctx *module.AttachContext) error {
|
||||
if rc, ok := h.inner.(module.Reconfigurer); ok {
|
||||
return rc.Reconfigure(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h hooked) Unwrap() module.Module {
|
||||
return h.inner
|
||||
}
|
||||
|
||||
@@ -4,17 +4,40 @@ import (
|
||||
"aego/app/assembly"
|
||||
"aego/engine/base"
|
||||
"aego/engine/module"
|
||||
"aego/modules/camera"
|
||||
"aego/modules/debug"
|
||||
inputmod "aego/modules/input"
|
||||
"aego/modules/physics"
|
||||
"aego/modules/render2d"
|
||||
"aego/modules/render3d"
|
||||
"aego/modules/scripting"
|
||||
"aego/modules/tween"
|
||||
"aego/script/stdlib"
|
||||
"aego/script/vm"
|
||||
)
|
||||
|
||||
func Catalog() assembly.Catalog {
|
||||
return assembly.Catalog{
|
||||
{ID: base.CoreID, New: func(assembly.Options) module.Module { return base.Core{} }},
|
||||
{ID: inputmod.ID, New: func(assembly.Options) module.Module { return inputmod.New() }},
|
||||
{ID: render2d.ID, New: func(assembly.Options) module.Module { return render2d.New() }},
|
||||
{ID: scripting.ID, New: func(o assembly.Options) module.Module {
|
||||
return scripting.New(stdlib.Options{Seed: o.Seed, Testing: o.Testing, Input: o.Input})
|
||||
return scripting.New(stdlib.Options{Seed: o.Seed, Testing: o.Testing})
|
||||
}},
|
||||
{ID: physics.ID, New: func(assembly.Options) module.Module { return physics.New() }},
|
||||
{ID: debug.ID, New: func(assembly.Options) module.Module { return debug.New() }},
|
||||
{ID: tween.ID, Requires: []string{scripting.ID}, New: func(assembly.Options) module.Module { return tween.New() }},
|
||||
{ID: camera.ID, Requires: []string{scripting.ID}, New: func(assembly.Options) module.Module { return camera.New() }},
|
||||
{ID: render3d.ID, New: func(assembly.Options) module.Module { return render3d.New() }},
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
SandboxBudget = 2_000_000
|
||||
SandboxHeap = 64 << 20
|
||||
SandboxNodes = 10_000
|
||||
)
|
||||
|
||||
func SandboxVM() vm.Options {
|
||||
return vm.Options{Budget: SandboxBudget, MaxHeap: SandboxHeap}
|
||||
}
|
||||
|
||||
+48
-2
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
package crash
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBundleHidesHome(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
t.Setenv("HOME", home)
|
||||
root := filepath.Join(home, "game")
|
||||
dir, err := Write(Bundle{Root: root, Kind: "panic", Where: "panel inspector", Panic: "boom in " + root, Stack: []byte(root + "/x.go:1")})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, f := range []string{"info.aetext", "go.stack"} {
|
||||
data, err := os.ReadFile(filepath.Join(dir, f))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(data), home) || !strings.Contains(string(data), "~/game") {
|
||||
t.Fatalf("%s is not sanitized: %s", f, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package docstool
|
||||
|
||||
import (
|
||||
"aego/app/playground"
|
||||
"aego/engine/docs"
|
||||
"aego/script/diag"
|
||||
)
|
||||
|
||||
func diagnoser(pg *playground.Playground) func(string, *docs.DiagInfo) {
|
||||
return func(code string, d *docs.DiagInfo) {
|
||||
got, err := pg.Reproduce(code)
|
||||
if err != nil || got.Code == "" {
|
||||
return
|
||||
}
|
||||
d.Severity, d.At, d.Message = got.Severity, got.File, got.Message
|
||||
d.Line, d.Col, d.EndLine, d.EndCol = got.Line, got.Col, got.EndLine, got.EndCol
|
||||
}
|
||||
}
|
||||
|
||||
// DiagnosticProblems lists codes whose example is missing or no longer produces the code.
|
||||
func DiagnosticProblems() []docs.Problem {
|
||||
pg, err := playground.New()
|
||||
if err != nil {
|
||||
return []docs.Problem{{ID: docs.DiagnosticsID, Error: true, Message: "cannot start the docs runtime: " + err.Error()}}
|
||||
}
|
||||
var out []docs.Problem
|
||||
for _, c := range diag.Codes() {
|
||||
if _, err := pg.Reproduce(c); err != nil {
|
||||
out = append(out, docs.Problem{ID: c, Error: true, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -138,7 +138,7 @@ func TestF1Refs(t *testing.T) {
|
||||
if err := os.WriteFile(path, []byte(probe), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reg, ws, err := build(p, nil)
|
||||
reg, ws, err := build(p, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -169,3 +169,17 @@ func TestF1Refs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiagnosticExamplesExported(t *testing.T) {
|
||||
if ps := DiagnosticProblems(); len(ps) > 0 {
|
||||
t.Fatalf("diagnostics examples: %v", ps)
|
||||
}
|
||||
reg, err := ExportRegistry(nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
e, ok := reg.Get("AES2001")
|
||||
if !ok || e.Diag == nil || e.Diag.Line == 0 || e.Diag.Severity != "error" || !strings.Contains(e.Diag.Message, "heigth") {
|
||||
t.Fatalf("AES2001 example was not diagnosed: %+v", e.Diag)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"aego/app/assembly"
|
||||
"aego/app/assembly/builtin"
|
||||
"aego/app/playground"
|
||||
"aego/engine/docs"
|
||||
"aego/engine/module"
|
||||
scriptsystems "aego/modules/scripting/systems"
|
||||
@@ -20,7 +21,7 @@ import (
|
||||
"aego/script/workspace"
|
||||
)
|
||||
|
||||
func build(p *project.Project, mods []module.Module) (*docs.Registry, *workspace.Workspace, error) {
|
||||
func build(p *project.Project, mods []module.Module, diagnose func(string, *docs.DiagInfo)) (*docs.Registry, *workspace.Workspace, error) {
|
||||
types, err := assembly.NewTypes()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -41,6 +42,7 @@ func build(p *project.Project, mods []module.Module) (*docs.Registry, *workspace
|
||||
return nil, nil, err
|
||||
}
|
||||
src := docs.SourcesOf(bm, types.Schema(), man)
|
||||
src.Diagnose = diagnose
|
||||
var ws *workspace.Workspace
|
||||
if p != nil {
|
||||
ws, err = workspace.Open(workspace.Config{Root: p.Root, Module: p.Module, Scripts: p.ScriptsDir(), Bindings: reg.Table(), Docs: bm})
|
||||
@@ -61,7 +63,17 @@ func build(p *project.Project, mods []module.Module) (*docs.Registry, *workspace
|
||||
}
|
||||
|
||||
func Registry(p *project.Project, mods []module.Module) (*docs.Registry, error) {
|
||||
r, _, err := build(p, mods)
|
||||
r, _, err := build(p, mods, nil)
|
||||
return r, err
|
||||
}
|
||||
|
||||
// ExportRegistry is Registry with the diagnostics examples compiled for their messages and positions.
|
||||
func ExportRegistry(p *project.Project, mods []module.Module) (*docs.Registry, error) {
|
||||
pg, err := playground.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, _, err := build(p, mods, diagnoser(pg))
|
||||
return r, err
|
||||
}
|
||||
|
||||
@@ -84,9 +96,9 @@ func WriteExport(reg *docs.Registry, title, path string, w io.Writer) error {
|
||||
return os.WriteFile(path, data, 0o644)
|
||||
}
|
||||
|
||||
func Check(reg *docs.Registry, strict bool, w io.Writer) int {
|
||||
func Check(reg *docs.Registry, strict bool, w io.Writer, extra ...docs.Problem) int {
|
||||
errs, warns := 0, 0
|
||||
for _, p := range reg.Check(strict) {
|
||||
for _, p := range append(reg.Check(strict), extra...) {
|
||||
level := "warn "
|
||||
if p.Error {
|
||||
level = "error"
|
||||
|
||||
+130
-26
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,11 @@ func Tool(p *project.Project, args []string, stdout, stderr io.Writer, mods ...m
|
||||
if err := fs.Parse(args[1:]); err != nil {
|
||||
return 2
|
||||
}
|
||||
reg, err := Registry(p, mods)
|
||||
build := Registry
|
||||
if args[0] == "export" {
|
||||
build = ExportRegistry
|
||||
}
|
||||
reg, err := build(p, mods)
|
||||
if err != nil {
|
||||
fmt.Fprintln(stderr, "aego:", err)
|
||||
return 1
|
||||
@@ -34,7 +38,7 @@ func Tool(p *project.Project, args []string, stdout, stderr io.Writer, mods ...m
|
||||
}
|
||||
return 0
|
||||
case "check":
|
||||
return Check(reg, *strict, stdout)
|
||||
return Check(reg, *strict, stdout, DiagnosticProblems()...)
|
||||
}
|
||||
fmt.Fprintf(stderr, "unknown docs command %q\n", args[0])
|
||||
return 2
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package docstool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"aego/app/playground"
|
||||
"aego/engine/docs"
|
||||
)
|
||||
|
||||
const tryFrames = 60
|
||||
|
||||
func CheckTry(reg *docs.Registry, w io.Writer) int {
|
||||
pg, err := playground.New()
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "try: cannot start the docs runtime:", err)
|
||||
return 1
|
||||
}
|
||||
pg.SetGame(playground.GameConfig{Headless: true, FrameTime: 1.0 / 60})
|
||||
defer func() {
|
||||
if g := pg.Game(); g != nil {
|
||||
g.Close()
|
||||
}
|
||||
}()
|
||||
sandbox := map[string]bool{}
|
||||
for _, e := range pg.TryScenes() {
|
||||
sandbox[e.ID] = true
|
||||
}
|
||||
bad := 0
|
||||
for _, e := range reg.All() {
|
||||
if e.Try == "" {
|
||||
continue
|
||||
}
|
||||
var msgs []string
|
||||
add := func(msg string) {
|
||||
for _, m := range msgs {
|
||||
if m == msg || strings.Contains(m, msg) || strings.Contains(msg, m) {
|
||||
return
|
||||
}
|
||||
}
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
switch res := pg.PlayScene(e.ID, []byte(e.Try)); {
|
||||
case !sandbox[e.ID]:
|
||||
add("belongs to a module the docs runtime does not include")
|
||||
case !res.Started:
|
||||
add("does not load: " + res.Error)
|
||||
default:
|
||||
if err := pg.Game().Step(tryFrames); err != nil {
|
||||
add("stops the engine: " + err.Error())
|
||||
}
|
||||
}
|
||||
poll := pg.Poll()
|
||||
for _, l := range poll.Log {
|
||||
if l.Level != "INFO" && l.Level != "DEBUG" {
|
||||
add(l.Text)
|
||||
}
|
||||
}
|
||||
for _, f := range poll.Faults {
|
||||
add(f.Code + " " + f.Message)
|
||||
}
|
||||
for _, m := range msgs {
|
||||
fmt.Fprintf(w, "%s: error: try scene %s\n", e.ID, m)
|
||||
}
|
||||
bad += len(msgs)
|
||||
}
|
||||
pg.Stop()
|
||||
return bad
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,84 @@
|
||||
//go:build aego_editor
|
||||
|
||||
package docsview
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"aego/editor/core/docsession"
|
||||
"aego/scene/sceneio"
|
||||
)
|
||||
|
||||
type request struct {
|
||||
Scene string `json:"scene"`
|
||||
Select string `json:"select"`
|
||||
Component string `json:"component"`
|
||||
Field string `json:"field"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
type Reply struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
Values map[string]string `json:"values,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
State *docsession.State `json:"state,omitempty"`
|
||||
Log []Line `json:"log,omitempty"`
|
||||
}
|
||||
|
||||
func (a *App) Call(method string, payload []byte) Reply {
|
||||
var req request
|
||||
if len(payload) > 0 {
|
||||
if err := json.Unmarshal(payload, &req); err != nil {
|
||||
return Reply{Error: err.Error()}
|
||||
}
|
||||
}
|
||||
s := a.session
|
||||
var err error
|
||||
switch method {
|
||||
case "load":
|
||||
err = s.Load([]byte(req.Scene), req.Select)
|
||||
case "reset":
|
||||
err = s.Reset()
|
||||
case "text":
|
||||
case "play":
|
||||
s.TogglePause()
|
||||
case "undo":
|
||||
err = s.Undo()
|
||||
case "redo":
|
||||
err = s.Redo()
|
||||
case "set":
|
||||
err = s.Set(req.Component, req.Field, req.Value)
|
||||
case "state":
|
||||
st := s.State()
|
||||
return Reply{State: &st}
|
||||
case "log":
|
||||
out := Reply{Log: a.lines}
|
||||
a.lines = nil
|
||||
return out
|
||||
default:
|
||||
return Reply{Error: "unknown method " + strconv.Quote(method)}
|
||||
}
|
||||
if err != nil {
|
||||
return Reply{Error: err.Error()}
|
||||
}
|
||||
st := s.State()
|
||||
return Reply{Text: string(s.Text()), Values: Values(s.Text()), State: &st}
|
||||
}
|
||||
|
||||
// Values flattens a scene file into the field keys the docs scene tree uses.
|
||||
func Values(text []byte) map[string]string {
|
||||
nodes, err := sceneio.Outline(text)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return sceneio.OutlineValues(nodes)
|
||||
}
|
||||
|
||||
func (a *App) Reply(method, payload string) string {
|
||||
out, err := json.Marshal(a.Call(method, []byte(payload)))
|
||||
if err != nil {
|
||||
return `{"error":"cannot encode the reply"}`
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//go:build aego_editor
|
||||
|
||||
package docsview
|
||||
|
||||
import (
|
||||
playmods "aego/app/assembly/playground"
|
||||
"aego/core/log"
|
||||
"aego/editor/core/context"
|
||||
"aego/engine"
|
||||
"aego/engine/module"
|
||||
"aego/scene"
|
||||
"aego/script/bind"
|
||||
"aego/script/bytecode"
|
||||
scriptruntime "aego/script/runtime"
|
||||
)
|
||||
|
||||
type scriptHost struct {
|
||||
reg *bind.Registry
|
||||
types *scene.Types
|
||||
man *module.Manifest
|
||||
log *log.Logger
|
||||
mods []*bytecode.Module
|
||||
}
|
||||
|
||||
func (h *scriptHost) Errors() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (h *scriptHost) Attach(w *engine.World, s *scene.Scene) (func(), error) {
|
||||
rt := scriptruntime.New(scriptruntime.Options{Bindings: h.reg, Schema: h.types.Schema(), Log: h.log, VM: playmods.SandboxVM()})
|
||||
if err := rt.Link(h.mods); err != nil {
|
||||
rt.Close()
|
||||
return nil, err
|
||||
}
|
||||
w.SetService(scriptruntime.ServiceID, rt)
|
||||
if err := rt.Instantiate(s); err != nil {
|
||||
w.RemoveService(scriptruntime.ServiceID)
|
||||
rt.Close()
|
||||
return nil, err
|
||||
}
|
||||
detach, err := h.man.Attach(&module.AttachContext{World: w, Scene: s, Log: h.log, Editor: true, Play: true})
|
||||
if err != nil {
|
||||
w.RemoveService(scriptruntime.ServiceID)
|
||||
rt.Close()
|
||||
return nil, err
|
||||
}
|
||||
return func() {
|
||||
detach()
|
||||
w.RemoveService(scriptruntime.ServiceID)
|
||||
rt.Close()
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *scriptHost) Problems() ([]context.ScriptProblem, uint64) {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
func (h *scriptHost) Stats() (context.ScriptStats, bool) {
|
||||
return context.ScriptStats{}, false
|
||||
}
|
||||
|
||||
func (h *scriptHost) Status() (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (h *scriptHost) Watch(*engine.World, scene.NodeHandle, string, string, int) (string, float64, bool, bool) {
|
||||
return "", 0, false, false
|
||||
}
|
||||
|
||||
var _ context.ScriptHost = (*scriptHost)(nil)
|
||||
@@ -0,0 +1,97 @@
|
||||
package edit
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"aego/app/crash"
|
||||
"aego/core/ids"
|
||||
"aego/core/log"
|
||||
"aego/editor/core/context"
|
||||
"aego/scene"
|
||||
"aego/scene/sceneio"
|
||||
"aego/script/runtime"
|
||||
"aego/script/vm"
|
||||
)
|
||||
|
||||
func scriptStack(f *vm.Fault) string {
|
||||
var b strings.Builder
|
||||
for _, fr := range f.Stack {
|
||||
b.WriteString(fr.File + ":" + strconv.Itoa(fr.Line) + " " + fr.Function + "\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func names(s *scene.Scene) func(ids.NodeID) string {
|
||||
return func(id ids.NodeID) string {
|
||||
if h := s.Handle(id); !h.IsZero() && s.IsAlive(h) {
|
||||
return s.Path(h)
|
||||
}
|
||||
return id.String()[:8]
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) moduleIDs() string {
|
||||
var out []string
|
||||
for _, m := range s.Modules.Modules {
|
||||
out = append(out, m.ID)
|
||||
}
|
||||
return strings.Join(out, ",")
|
||||
}
|
||||
|
||||
func (s *Session) crash(kind, where string, rec any, stack []byte, script string) {
|
||||
defer func() { recover() }()
|
||||
b := crash.Bundle{
|
||||
Root: s.Project.Root, Kind: kind, Where: where, Panic: rec, Stack: stack, Script: script,
|
||||
Trace: s.Engine.Trace(), Log: s.ring,
|
||||
Info: [][2]string{{"project", s.Project.Name}, {"gpu", s.Engine.Caps().Renderer}, {"modules", s.moduleIDs()}},
|
||||
}
|
||||
var ctx *context.Editor
|
||||
if s.App != nil {
|
||||
ctx = s.App.Context()
|
||||
}
|
||||
if ctx != nil {
|
||||
ctx.Docs.WriteRecovery()
|
||||
if pc := s.App.Play(); pc != nil && pc.World() != nil {
|
||||
b.Events = pc.Recorder()
|
||||
if sc := pc.Scene(); sc != nil {
|
||||
b.Names = names(sc)
|
||||
if s.crashScene {
|
||||
b.Scene = sceneio.Save(sc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dir, err := crash.Write(b)
|
||||
if err != nil {
|
||||
s.log.Error("cannot write the crash bundle", log.F("err", err.Error()))
|
||||
return
|
||||
}
|
||||
s.log.Error("crash bundle written", log.F("dir", dir), log.F("where", where))
|
||||
if ctx != nil {
|
||||
ctx.Notify(context.LevelError, "Crash report saved to "+dir)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) scriptPanic(boundary string, v any, stack []byte) {
|
||||
s.crash("panic", boundary, v, stack, "")
|
||||
}
|
||||
|
||||
func (s *Session) fault(rep runtime.FaultReport) {
|
||||
if rep.Fault != nil && rep.Fault.Code == vm.CodeNativePanic {
|
||||
s.crash("fault", rep.Type, rep.Fault.Message, []byte(rep.Fault.Detail), scriptStack(rep.Fault))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) spike() {
|
||||
if s.spikeMs <= 0 {
|
||||
return
|
||||
}
|
||||
ms := s.Engine.Stats().FrameTime * 1000
|
||||
now := s.Engine.Platform().Now()
|
||||
if ms < s.spikeMs || now-s.lastSpike < 10 {
|
||||
return
|
||||
}
|
||||
s.lastSpike = now
|
||||
s.crash("spike", strconv.FormatFloat(ms, 'f', 1, 64)+" ms frame", nil, nil, "")
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -116,12 +116,8 @@ func TestPlayLeavesEditSceneByteIdentical(t *testing.T) {
|
||||
t.Fatal("Spin is not registered")
|
||||
}
|
||||
speed, _ := td.Field("speed")
|
||||
add := &commands.AddComponent{
|
||||
Node: spinner,
|
||||
Type: td.ID,
|
||||
Name: td.FullName,
|
||||
Values: []commands.FieldValue{{Field: speed.ID, Value: schema.Float32(3)}},
|
||||
}
|
||||
add := commands.NewMutation("Add "+td.FullName, commands.EnvFor(es),
|
||||
commands.AddComp(es, spinner, td, []commands.FieldValue{{Field: speed.ID, Value: schema.Float32(3)}}))
|
||||
if err := d.History.Exec(add); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package edit
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"aego/app/bootstrap"
|
||||
"aego/app/export"
|
||||
"aego/app/projects"
|
||||
"aego/asset/pipeline"
|
||||
"aego/editor/core/services"
|
||||
"aego/engine/problems"
|
||||
"aego/project"
|
||||
)
|
||||
|
||||
type exporter struct {
|
||||
p *project.Project
|
||||
pl *pipeline.Pipeline
|
||||
engine string
|
||||
}
|
||||
|
||||
func newExporter(p *project.Project, pl *pipeline.Pipeline) services.Exporter {
|
||||
if pl == nil {
|
||||
return nil
|
||||
}
|
||||
root, err := projects.EngineRoot("")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &exporter{p: p, pl: pl, engine: root}
|
||||
}
|
||||
|
||||
func (x *exporter) Targets() []string {
|
||||
return export.Targets()
|
||||
}
|
||||
|
||||
func (x *exporter) Out(target, profile string) string {
|
||||
t, err := export.ParseTarget(target)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
prof, err := export.ParseProfile(profile)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return export.DefaultOut(x.p, t, prof)
|
||||
}
|
||||
|
||||
func (x *exporter) Prepare() any {
|
||||
return export.Take(x.pl)
|
||||
}
|
||||
|
||||
func (x *exporter) Check(prep any, profile string) []problems.Diagnostic {
|
||||
snap, ok := prep.(*export.Snapshot)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
prof, err := export.ParseProfile(profile)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return export.Check(x.p, snap, prof, bootstrap.NeedsNative(x.p))
|
||||
}
|
||||
|
||||
func (x *exporter) Export(prep any, req services.ExportRequest, progress func(float32, string)) (services.ExportReport, error) {
|
||||
snap, _ := prep.(*export.Snapshot)
|
||||
rep, err := export.Export(export.Options{
|
||||
Project: x.p, Engine: x.engine, Target: req.Target, Profile: req.Profile, Out: req.Out,
|
||||
Snapshot: snap, Progress: progress,
|
||||
})
|
||||
var out services.ExportReport
|
||||
if rep != nil {
|
||||
out = services.ExportReport{Out: rep.Out, Lines: rep.Lines(), Issues: rep.Diagnostics, Blocked: rep.Blocked}
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (x *exporter) Reveal(path string) error {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
cmd = exec.Command("open", path)
|
||||
case "windows":
|
||||
cmd = exec.Command("explorer", path)
|
||||
default:
|
||||
cmd = exec.Command("xdg-open", path)
|
||||
}
|
||||
return cmd.Start()
|
||||
}
|
||||
+59
-4
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
package edit
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dir, err := os.MkdirTemp("", "aego-config-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
os.Setenv("HOME", dir)
|
||||
case "windows":
|
||||
os.Setenv("AppData", dir)
|
||||
default:
|
||||
os.Setenv("XDG_CONFIG_HOME", dir)
|
||||
}
|
||||
code := m.Run()
|
||||
os.RemoveAll(dir)
|
||||
os.Exit(code)
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func TestColliderFitsSprite(t *testing.T) {
|
||||
}
|
||||
for _, name := range []string{"Crate", "Bare"} {
|
||||
id := es.ID(es.Find(es.Root(), name))
|
||||
if err := d.History.Exec(&commands.AddComponent{Node: id, Type: td.ID, Name: td.FullName, OnAdd: ctx.OnAdd(td)}); err != nil {
|
||||
if err := d.History.Exec(commands.NewMutation("Add "+td.FullName, commands.WithOnAdd(commands.EnvFor(es), ctx.OnAdd), commands.AddComp(es, id, td, nil))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ func TestProfilerSectionsFollowPlay(t *testing.T) {
|
||||
}
|
||||
prof := s.App.Profiler()
|
||||
drawPanel(s, prof)
|
||||
expectSections(t, prof.Sections(), "Frame")
|
||||
expectSections(t, prof.Sections(), "Frame", "Memory")
|
||||
pc := s.App.Play()
|
||||
if err := pc.Start(d); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -179,7 +179,7 @@ func TestProfilerSectionsFollowPlay(t *testing.T) {
|
||||
s.Step()
|
||||
}
|
||||
drawPanel(s, prof)
|
||||
expectSections(t, prof.Sections(), "Frame", "Game", "Scripts", "Physics")
|
||||
expectSections(t, prof.Sections(), "Frame", "Memory", "Game", "Scripts", "Physics")
|
||||
if _, err := ctx.Docs.Open("scenes/other.aescene"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -189,5 +189,5 @@ func TestProfilerSectionsFollowPlay(t *testing.T) {
|
||||
pc.Stop()
|
||||
s.Step()
|
||||
drawPanel(s, prof)
|
||||
expectSections(t, prof.Sections(), "Frame")
|
||||
expectSections(t, prof.Sections(), "Frame", "Memory")
|
||||
}
|
||||
|
||||
@@ -51,12 +51,8 @@ func openSpinner(t *testing.T) *spinFixture {
|
||||
}
|
||||
fd, _ := td.Field("speed")
|
||||
f.typ, f.speed = td.ID, fd.ID
|
||||
add := &commands.AddComponent{
|
||||
Node: f.spinner,
|
||||
Type: td.ID,
|
||||
Name: td.FullName,
|
||||
Values: []commands.FieldValue{{Field: fd.ID, Value: schema.Float32(3)}},
|
||||
}
|
||||
add := commands.NewMutation("Add "+td.FullName, commands.EnvFor(es),
|
||||
commands.AddComp(es, f.spinner, td, []commands.FieldValue{{Field: fd.ID, Value: schema.Float32(3)}}))
|
||||
if err := d.History.Exec(add); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+11
-1
@@ -36,6 +36,8 @@ type scriptHost struct {
|
||||
probRev uint64
|
||||
probs []context.ScriptProblem
|
||||
stats context.ScriptStats
|
||||
fault func(runtime.FaultReport)
|
||||
panicked func(boundary string, v any, stack []byte)
|
||||
}
|
||||
|
||||
func (h *scriptHost) channel() *log.ChannelLogger {
|
||||
@@ -158,7 +160,7 @@ func (h *scriptHost) Attach(w *engine.World, s *scene.Scene) (func(), error) {
|
||||
if err := h.sync(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rt := runtime.New(runtime.Options{Bindings: h.bindings, Schema: h.schema, Log: h.log})
|
||||
rt := runtime.New(runtime.Options{Bindings: h.bindings, Schema: h.schema, Log: h.log, OnFault: h.fault, OnPanic: h.panicked})
|
||||
if err := rt.Link(h.prog.Ordered()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -193,3 +195,11 @@ func (h *scriptHost) Attach(w *engine.World, s *scene.Scene) (func(), error) {
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *scriptHost) Watch(w *engine.World, node scene.NodeHandle, script, field string, lane int) (string, float64, bool, bool) {
|
||||
rt, ok := engine.ServiceOf[*runtime.Runtime](w, runtime.ServiceID)
|
||||
if !ok || rt == nil {
|
||||
return "", 0, false, false
|
||||
}
|
||||
return rt.WatchField(node, script, field, lane)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user