45 Commits
Author SHA1 Message Date
sanspie 4068e00375 install: read SHA256SUMS.txt first and fetch the archive by its checksum, past stale proxy caches
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci / checks (push) Successful in 15m44s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 17m13s
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / release (push) Successful in 17m39s
2026-10-21 03:56:04 +03:00
sanspie 914cf39150 release: Linux files and checksums first, keep going when a Windows upload fails
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / release (push) Successful in 20m29s
ci / checks (push) Successful in 14m57s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 16m18s
2026-10-21 02:37:55 +03:00
sanspie dede4d8af3 release: record upload failures in the release body
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / checks (push) Successful in 13m53s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 15m0s
ci / release (push) Failing after 15m2s
2026-10-21 02:32:02 +03:00
sanspie b6dd46f523 Stage 10a follow-up: camera scene tests, rewind check, value Adjust
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci / checks (push) Successful in 14m14s
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 16m1s
ci / release (push) Failing after 14m38s
- camera.NewService and SetAccessibility; AttachWorld wires the accessibility settings
- tests/camera: priorities, blends, cuts, zones, follow, bounds, outputs, shake, zero allocations, BrainStep benchmark
- integration: rewind restores the camera state across a zone change
- camera2d.Adjuster passes the view by value (no per-frame allocation), D304 updated
- modules/camera/module.mk: test-camera in CI, bench-camera
2026-10-21 01:32:28 +03:00
sanspie 9d32052624 release: one SHA256SUMS.txt and a zipped Windows installer, the attachment types Gitea accepts
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci / release (push) Failing after 14m32s
ci / checks (push) Successful in 14m14s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 16m8s
2026-10-21 01:31:53 +03:00
sanspie e636801578 release: upload straight to the Gitea server with retries
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / release (push) Failing after 14m22s
ci / checks (push) Successful in 14m30s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 17m14s
2026-10-21 00:29:58 +03:00
sanspie 01fdeef7a8 Aego 0.9.0-beta.1
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / checks (push) Successful in 14m2s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 16m23s
ci / release (push) Failing after 14m1s
2026-10-20 23:57:45 +03:00
sanspie 71354f6a60 install scripts: fall back to nightly, explain when nothing is published 2026-10-20 23:55:52 +03:00
sanspie 8ae6832503 edge sync: keep the destination when resuming downloads 2026-10-20 20:11:47 +03:00
sanspie 183d75ed50 gendocs: nav golden without the editor-only tour 2026-10-20 20:07:22 +03:00
sanspie 19edcbf9b2 render3d in the web sandbox without the OBJ importer; 3D try scenes select a shape 2026-10-20 19:49:47 +03:00
sanspie 8ed77a7638 Docs: no favicon request 2026-10-20 19:48:24 +03:00
sanspie ca3c9bf2e7 Docs: 3D Try it scenes, render3d in the sandbox, smarter code language detection 2026-10-20 19:43:38 +03:00
sanspie db59f117a4 deploy: short cache for the root page; edge notes in D303 2026-10-20 19:35:50 +03:00
sanspie 0d2c20da26 Docs: revision in the header, content-versioned assets; deploy passes the commit to the image 2026-10-20 19:31:14 +03:00
sanspie dd41ff15a5 deploy: edge serves the site and wasm from its own copy 2026-10-20 19:12:22 +03:00
sanspie 599e09a35d Docs: Try it on Physics 2D guide pages 2026-10-20 18:26:35 +03:00
sanspie 60614938be Release: Windows installer, install.ps1, component page layout 2026-10-20 18:20:18 +03:00
sanspie a60ca47d31 wip release windows 2026-10-20 18:13:57 +03:00
sanspie 00ae2418a0 Docs: more editor screenshots for the editor, prefab, tags, input, time and physics guides 2026-10-20 17:57:58 +03:00
sanspie 8fc9b4c287 Docs: highlighting for every code block, editor-only tour off the site, page icons, prefetch, field docs for physics, wider Try it, demo camera 2026-10-20 17:50:21 +03:00
sanspie b29934b51d deploy: short HTML cache the edge proxy can store 2026-10-20 17:29:55 +03:00
sanspie c475b42e5d gendocs: Script API index links to type pages; nav golden 2026-10-20 17:01:11 +03:00
sanspie b103816400 websmoke: Try smoke checks the scene tree 2026-10-20 17:00:46 +03:00
sanspie 487170dd9c depcheck: tween in the web sandbox 2026-10-20 16:46:52 +03:00
sanspie 57579356d6 Docs: streamed compile with tee'd progress, tween in the sandbox, console runs step module systems 2026-10-20 16:46:03 +03:00
sanspie d5377410e4 Docs: HTTP cache for the docs runtime, autostart after a first start, cheaper warm loads 2026-10-20 16:29:16 +03:00
sanspie f6c83a6e0c Docs: compile-checked examples for modules outside the sandbox, HasScript fix 2026-10-20 16:03:58 +03:00
sanspie 8d7712d671 Docs: runnable Script API module examples and Run it blocks 2026-10-20 16:03:26 +03:00
sanspie 63980faccb Docs: diagnostics with checked examples 2026-10-20 16:00:13 +03:00
sanspie 0041e7e639 Docs: loader streams compile from the original response 2026-10-20 15:59:50 +03:00
sanspie e334be569f Architecture: D301–D303, amendments for the docs overhaul 2026-10-20 15:49:46 +03:00
sanspie b488958444 Docs panel goldens with short-name links 2026-10-20 15:48:31 +03:00
sanspie 5875a65193 Docs: Try it for every 2D physics component, script examples on component pages, Script API index, smooth wheel scrolling on the web 2026-10-20 15:46:33 +03:00
sanspie 6ab66cb8ae Docs: scene tree, loading progress and cache, release builds, install page, editor screenshots 2026-10-20 15:36:03 +03:00
sanspie f8fe54e9c5 Docs: short-name cross references in the site and the editor docs panel, links from code 2026-10-20 15:17:10 +03:00
sanspie e4326e7e7c Docs: editor icons, editor highlighter, redesigned site and navigation 2026-10-20 15:14:55 +03:00
sanspie 6a22396467 Stage 9 complete
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci / checks (push) Successful in 22m2s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 23m5s
2026-10-20 14:06:22 +03:00
sanspie 21e03128a8 Stage 9.6d2: MCP server and external edits in the editor (D297, D300) 2026-10-20 13:56:45 +03:00
sanspie e5b1964f3e Stage 9.6d1: agent layer and CLI (D299) 2026-10-20 13:48:01 +03:00
sanspie cffa66514b Stage 9.6c2: prefab apply and data editing without the editor (D298)
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / checks (push) Successful in 21m2s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 22m8s
2026-10-20 13:05:10 +03:00
sanspie 2744cff481 Stage 9.6c1: project refs and refactorings without the editor (D298)
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 23m31s
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci / checks (push) Successful in 21m57s
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
2026-10-20 12:12:18 +03:00
sanspie 91052a54db make lint: staticcheck the editor build
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / checks (push) Successful in 16m26s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 17m48s
2026-10-20 12:06:56 +03:00
sanspie 7f4d87ef2c Stage 9: gameplay toolkit, export and web, agent tooling 2026-10-20 12:06:56 +03:00
sanspie 6cc0673082 CI on master, docs auto-deploy and repo link, macOS build fixes
ci / checks (push) Successful in 13m36s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 14m40s
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
2026-10-16 12:10:43 +03:00
1202 changed files with 93275 additions and 4166 deletions
+1
View File
@@ -5,3 +5,4 @@ site
exports
**/.aego
*.test
.claude
File diff suppressed because it is too large Load Diff
+40 -5
View File
@@ -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
+6
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
+115 -8
View File
File diff suppressed because it is too large Load Diff
+146
View File
File diff suppressed because it is too large Load Diff
+85
View File
@@ -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
+194
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+180
View File
File diff suppressed because it is too large Load Diff
+66
View File
@@ -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
}
+237
View File
File diff suppressed because it is too large Load Diff
+393
View File
File diff suppressed because it is too large Load Diff
+166
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-2
View File
@@ -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 {
+59 -3
View File
@@ -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()}
}
+1 -1
View File
@@ -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()}
}
+7
View File
@@ -0,0 +1,7 @@
package builtin
import "aego/app/assembly"
func Runtime() assembly.Catalog {
return Default()
}
+5 -1
View File
@@ -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 {
+10 -1
View File
@@ -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)
}
+11
View File
@@ -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
}
+24 -1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+140
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -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)
}
}
}
+33
View File
@@ -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
}
+15 -1
View File
@@ -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)
}
}
+16 -4
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -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
+70
View File
@@ -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
}
+417
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+84
View File
@@ -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)
}
+70
View File
@@ -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)
+97
View File
@@ -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, "")
}
+35
View File
File diff suppressed because it is too large Load Diff
+2 -6
View File
@@ -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)
}
+89
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -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)
}
+1 -1
View File
@@ -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)
}
}
+3 -3
View File
@@ -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")
}
+2 -6
View File
@@ -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
View File
@@ -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)
}
+155
View File
File diff suppressed because it is too large Load Diff
+208
View File
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