18 Commits
Author SHA1 Message Date
sanspie 3f5584d062 Stage 9.5b3b: component, settings and CLI reference (D292) 2026-10-20 00:11:43 +03:00
sanspie 6ce72aa1ec Stage 9.5b3a: docs IA and site shell (D292) 2026-10-19 23:27:11 +03:00
sanspie 9ee2b0b05c Stage 9.5b2: playground with Game View (D291) 2026-10-19 23:10:55 +03:00
sanspie 99dacd7a75 Stage 9.5b1: web platform and WebGL2 backend (D289–D290) 2026-10-19 22:47:08 +03:00
sanspie 5447383f2b gitignore: aego-runtime binary and .syso resources 2026-10-19 20:39:03 +03:00
sanspie 6745d027fb Stage 9.5a: export v1 (D286–D288) 2026-10-19 20:27:58 +03:00
sanspie e7dd01371e script/abi: Parse accepts the untyped Handle<> the encoder writes 2026-10-19 20:27:40 +03:00
sanspie 68802195b6 Stage 9.0: editor workflow 2026-10-19 20:27:40 +03:00
sanspie 7cba364aed Stage 9.4d4: aego test --determinism 2026-10-19 02:18:58 +03:00
sanspie 35e9849cae Stage 9.4d3: keyframes 2026-10-19 02:15:10 +03:00
sanspie f21d455d68 Stage 9.4d3a: CloneInto, spawn pool 2026-10-19 01:51:39 +03:00
sanspie d1a16d8208 Stage 9.4d2: rewind 2026-10-19 01:47:39 +03:00
sanspie 2a1826f876 Stage 9.4d1: save games 2026-10-19 00:37:50 +03:00
sanspie 3af2d5d977 Stage 9.4c: data assets, validators 2026-10-18 22:58:02 +03:00
sanspie b61a1656ad Stage 9.4b: signals, tags, spawn 2026-10-18 22:58:02 +03:00
sanspie 0fbf22a090 Stage 9.4a: curves, tween, timers
core/curve with presets, keys, wraps; KindCurve in schema, scenes, prefabs and the inspector curve editor.
modules/tween: tweens and timers in per-world pools, game and unscaled domains, script field targets via contracts/scriptfields.
Host handle types (abi.Handle, bind.Handle, manifest v5), yield on awaitable handles via contracts/scriptwait.
Script modules aego/curve, aego/tween, aego/timer; platformer enemy uses a cooldown and a fade-out tween.
Docs: tween.md, curves.md, language reference; golden for the curve editor.
2026-10-18 21:32:39 +03:00
sanspie 5e763aa73c Stage 9.2–9.3: input and settings 2026-10-18 21:32:39 +03:00
sanspie 6093ca9d8b Stage 9.1: time and debug 2026-10-17 23:14:21 +03:00
625 changed files with 2732 additions and 49749 deletions
-1
View File
@@ -5,4 +5,3 @@ site
exports
**/.aego
*.test
.claude
+1 -41
View File
@@ -3,7 +3,6 @@ name: ci
on:
push:
branches: [master]
tags: ["v*"]
pull_request:
permissions:
@@ -37,10 +36,6 @@ jobs:
- run: go vet ./platform/sdl
- run: make lint
- run: make depcheck
- name: packaging tools
run: sudo apt-get install -y nsis squashfs-tools
- name: installer dry run
run: make nsis-check
build:
strategy:
@@ -116,39 +111,4 @@ jobs:
- name: example project
run: |
/tmp/aego check examples/native-rotator
/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
/tmp/aego run examples/native-rotator --headless --frames 60
+14 -183
View File
File diff suppressed because one or more lines are too long
+7 -71
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
@@ -1,85 +0,0 @@
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
@@ -1,66 +0,0 @@
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
+1 -11
View File
@@ -6,11 +6,9 @@ import (
"aego/engine/module"
"aego/modules/accessibility"
"aego/modules/audio"
"aego/modules/camera"
"aego/modules/dataasset"
"aego/modules/debug"
"aego/modules/display"
"aego/modules/gameplay"
inputmod "aego/modules/input"
"aego/modules/physics"
"aego/modules/render2d"
@@ -73,14 +71,6 @@ 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 gameplayEntry() assembly.Entry {
return assembly.Entry{ID: gameplay.ID, Requires: []string{physics.ID, scripting.ID}, New: func(assembly.Options) module.Module { return gameplay.New() }}
}
func dataEntry() assembly.Entry {
return assembly.Entry{ID: dataasset.ID, New: func(assembly.Options) module.Module { return dataasset.New() }}
}
@@ -95,5 +85,5 @@ func savegameEntry() assembly.Entry {
func Default() assembly.Catalog {
return assembly.Catalog{core(), dataEntry(), inputEntry(), render2dEntry(), render3dEntry(), scriptEntry(), physicsEntry(), debugEntry(),
displayEntry(), audioEntry(), accessibilityEntry(), settingsEntry(), tweenEntry(), cameraEntry(), gameplayEntry(), spawnEntry(), savegameEntry()}
displayEntry(), audioEntry(), accessibilityEntry(), settingsEntry(), tweenEntry(), spawnEntry(), savegameEntry()}
}
+1 -1
View File
@@ -3,5 +3,5 @@ package builtin
import "aego/app/assembly"
func Headless() assembly.Catalog {
return assembly.Catalog{core(), dataEntry(), inputEntry(), scriptEntry(), physicsEntry(), debugEntry(), displayEntry(), audioEntry(), accessibilityEntry(), settingsEntry(), tweenEntry(), cameraEntry(), spawnEntry(), savegameEntry()}
return assembly.Catalog{core(), dataEntry(), inputEntry(), scriptEntry(), physicsEntry(), debugEntry(), displayEntry(), audioEntry(), accessibilityEntry(), settingsEntry(), tweenEntry(), spawnEntry(), savegameEntry()}
}
-6
View File
@@ -2,10 +2,6 @@ package editors
import (
"aego/app/assembly"
"aego/modules/audio"
audioeditor "aego/modules/audio/editor"
"aego/modules/camera"
cameraeditor "aego/modules/camera/editor"
"aego/modules/debug"
debugeditor "aego/modules/debug/editor"
inputmod "aego/modules/input"
@@ -24,7 +20,5 @@ func With(c assembly.Catalog) assembly.Catalog {
With(render2d.ID, render2deditor.Hook).
With(render3d.ID, render3deditor.Hook).
With(physics.ID, physicseditor.Hook).
With(camera.ID, cameraeditor.Hook).
With(audio.ID, audioeditor.Hook).
With(debug.ID, debugeditor.Hook)
}
-25
View File
@@ -4,20 +4,12 @@ import (
"aego/app/assembly"
"aego/engine/base"
"aego/engine/module"
"aego/modules/audio"
"aego/modules/camera"
"aego/modules/debug"
"aego/modules/gameplay"
inputmod "aego/modules/input"
"aego/modules/physics"
"aego/modules/render2d"
"aego/modules/render3d"
"aego/modules/savegame"
"aego/modules/scripting"
"aego/modules/spawn"
"aego/modules/tween"
"aego/script/stdlib"
"aego/script/vm"
)
func Catalog() assembly.Catalog {
@@ -30,22 +22,5 @@ func Catalog() assembly.Catalog {
}},
{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: gameplay.ID, Requires: []string{physics.ID, scripting.ID}, New: func(assembly.Options) module.Module { return gameplay.New() }},
{ID: render3d.ID, New: func(assembly.Options) module.Module { return render3d.New() }},
{ID: spawn.ID, New: func(assembly.Options) module.Module { return spawn.New() }},
{ID: savegame.ID, Requires: []string{scripting.ID, spawn.ID}, New: func(assembly.Options) module.Module { return savegame.New() }},
{ID: audio.ID, New: func(assembly.Options) module.Module { return audio.New() }},
}
}
const (
SandboxBudget = 2_000_000
SandboxHeap = 64 << 20
SandboxNodes = 10_000
)
func SandboxVM() vm.Options {
return vm.Options{Budget: SandboxBudget, MaxHeap: SandboxHeap}
}
-61
View File
@@ -1,61 +0,0 @@
package playground
import (
"io/fs"
"path"
"sort"
"strings"
"time"
"aego/contracts/persistence"
"aego/engine"
"aego/platform"
"aego/save"
)
// UseMemorySaves gives e a save store kept in memory, for runs in a browser page that must not touch the visitor's storage.
func UseMemorySaves(e *engine.Engine) {
e.SetService(persistence.StoreID, save.NewStoreFS("saves", &memUser{files: map[string][]byte{}}))
}
type memUser struct {
files map[string][]byte
}
func (m *memUser) ReadFile(p string) ([]byte, error) {
data, ok := m.files[path.Clean(p)]
if !ok {
return nil, fs.ErrNotExist
}
return append([]byte(nil), data...), nil
}
func (m *memUser) WriteFile(p string, data []byte) error {
m.files[path.Clean(p)] = append([]byte(nil), data...)
return nil
}
func (m *memUser) Remove(p string) error {
delete(m.files, path.Clean(p))
return nil
}
func (m *memUser) Stat(p string) (platform.UserEntry, error) {
data, ok := m.files[path.Clean(p)]
if !ok {
return platform.UserEntry{}, fs.ErrNotExist
}
return platform.UserEntry{Name: path.Base(p), Size: int64(len(data)), Mod: time.Time{}}, nil
}
func (m *memUser) List(dir string) ([]platform.UserEntry, error) {
prefix := path.Clean(dir) + "/"
var out []platform.UserEntry
for p, data := range m.files {
if rest, ok := strings.CutPrefix(p, prefix); ok && !strings.Contains(rest, "/") {
out = append(out, platform.UserEntry{Name: rest, Size: int64(len(data))})
}
}
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
return out, nil
}
-62
View File
@@ -1,62 +0,0 @@
package playground
import (
"encoding/base64"
"strings"
"aego/asset"
"aego/asset/loader"
"aego/core/ids"
"aego/script/bytecode"
"aego/vfs"
"aego/vfs/memfs"
)
// EncodeTry packs the compiled script of a try scene for a docs page: base64 modules joined by commas.
func EncodeTry(mods []*bytecode.Module) string {
parts := make([]string, len(mods))
for i, m := range mods {
parts[i] = base64.StdEncoding.EncodeToString(bytecode.Encode(m))
}
return strings.Join(parts, ",")
}
func DecodeTry(code string) ([]*bytecode.Module, error) {
if code == "" {
return nil, nil
}
var mods []*bytecode.Module
for _, part := range strings.Split(code, ",") {
b, err := base64.StdEncoding.DecodeString(part)
if err != nil {
return nil, err
}
m, err := bytecode.Decode(b)
if err != nil {
return nil, err
}
mods = append(mods, m)
}
return mods, nil
}
// MountTry gives the loader the prefabs of a try scene, keyed by asset id; none unmounts them.
func MountTry(l *loader.Loader, prefabs map[string]string) error {
idx := asset.NewIndex()
files := map[string][]byte{}
for key, text := range prefabs {
id, err := ids.ParseAssetID(key)
if err != nil {
return err
}
p := id.String() + ".aeprefab"
files[p] = []byte(text)
idx.Put(asset.Entry{ID: id, Kind: asset.ArtPrefab, Name: p, Source: p,
Artifacts: []asset.Artifact{{Kind: asset.ArtPrefab, Path: "try://" + p}}})
}
fs, err := vfs.New(vfs.Mount{Scheme: "try", ReadOnly: true, Source: memfs.New(files)})
if err != nil {
return err
}
return l.Mount(idx, fs)
}
-2
View File
@@ -9,7 +9,6 @@ import (
"sort"
"strings"
"aego/app/proc"
"aego/core/errs"
"aego/core/log"
"aego/project"
@@ -170,7 +169,6 @@ func Build(p *project.Project, t Target, mode Mode, engineVersion string, lg *lo
cmd := exec.Command("go", args...)
cmd.Dir = p.Root
cmd.Env = append(os.Environ(), "GOOS="+t.OS, "GOARCH="+t.Arch)
proc.Hide(cmd)
output, err := cmd.CombinedOutput()
if err != nil {
os.RemoveAll(outDir)
-3
View File
@@ -8,7 +8,6 @@ import (
"path/filepath"
"strings"
"aego/app/proc"
"aego/core/errs"
"aego/core/log"
"aego/project"
@@ -33,14 +32,12 @@ func RunIcons(p *project.Project, engineVersion string, out io.Writer) (int, err
defer os.Remove(bin)
build := exec.Command("go", "build", "-tags", strings.Join(t.Tags, ","), "-o", bin, "./.aego/generated/icons")
build.Dir = p.Root
proc.Hide(build)
if output, err := build.CombinedOutput(); err != nil {
return 2, errs.New(errs.BuildFailed, "native icons helper does not compile",
log.F("output", strings.TrimSpace(string(output))))
}
run := exec.Command(bin)
run.Dir, run.Stdout, run.Stderr = p.Root, out, out
proc.Hide(run)
err = run.Run()
var ee *exec.ExitError
if errors.As(err, &ee) {
-3
View File
@@ -6,7 +6,6 @@ import (
"path/filepath"
"strings"
"aego/app/proc"
"aego/core/errs"
"aego/core/log"
"aego/project"
@@ -53,7 +52,6 @@ func BuildManifest(p *project.Project, engineVersion string, lg *log.Logger) (st
defer os.Remove(bin)
build := exec.Command("go", "build", "-tags", strings.Join(t.Tags, ","), "-o", bin, "./.aego/generated/manifest")
build.Dir = p.Root
proc.Hide(build)
if output, err := build.CombinedOutput(); err != nil {
return "", errs.New(errs.BuildFailed, "native bindings helper does not compile",
log.F("output", strings.TrimSpace(string(output))))
@@ -61,7 +59,6 @@ func BuildManifest(p *project.Project, engineVersion string, lg *log.Logger) (st
tmp := out + ".tmp"
run := exec.Command(bin, tmp)
run.Dir = p.Root
proc.Hide(run)
if output, err := run.CombinedOutput(); err != nil {
os.Remove(tmp)
return "", errs.New(errs.BuildFailed, "native module Register failed",
-33
View File
@@ -1,33 +0,0 @@
package brand
import (
"bytes"
"embed"
"image"
"image/png"
"strconv"
)
//go:embed png/aego-*.png
var files embed.FS
var windowSizes = []int{16, 24, 32, 48, 64, 128, 256}
func PNG(n int) ([]byte, bool) {
data, err := files.ReadFile("png/aego-" + strconv.Itoa(n) + ".png")
return data, err == nil
}
func Window() []image.Image {
out := make([]image.Image, 0, len(windowSizes))
for _, n := range windowSizes {
data, ok := PNG(n)
if !ok {
continue
}
if img, err := png.Decode(bytes.NewReader(data)); err == nil {
out = append(out, img)
}
}
return out
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

-33
View File
@@ -1,33 +0,0 @@
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
}
+1 -15
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, nil)
reg, ws, err := build(p, nil)
if err != nil {
t.Fatal(err)
}
@@ -169,17 +169,3 @@ 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)
}
}
+4 -20
View File
@@ -9,7 +9,6 @@ import (
"aego/app/assembly"
"aego/app/assembly/builtin"
"aego/app/playground"
"aego/engine/docs"
"aego/engine/module"
scriptsystems "aego/modules/scripting/systems"
@@ -21,8 +20,7 @@ import (
"aego/script/workspace"
)
// build assembles the registry; with pg it also compiles the diagnostics examples and the try scripts.
func build(p *project.Project, mods []module.Module, pg *playground.Playground) (*docs.Registry, *workspace.Workspace, error) {
func build(p *project.Project, mods []module.Module) (*docs.Registry, *workspace.Workspace, error) {
types, err := assembly.NewTypes()
if err != nil {
return nil, nil, err
@@ -43,10 +41,6 @@ func build(p *project.Project, mods []module.Module, pg *playground.Playground)
return nil, nil, err
}
src := docs.SourcesOf(bm, types.Schema(), man)
if pg != nil {
src.Diagnose = diagnoser(pg)
src.CompileTry = pg.TryCode
}
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})
@@ -67,17 +61,7 @@ func build(p *project.Project, mods []module.Module, pg *playground.Playground)
}
func Registry(p *project.Project, mods []module.Module) (*docs.Registry, error) {
r, _, err := build(p, mods, nil)
return r, err
}
// ExportRegistry is Registry with the diagnostics examples compiled for their messages and positions, and the try scripts compiled.
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, pg)
r, _, err := build(p, mods)
return r, err
}
@@ -100,9 +84,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, extra ...docs.Problem) int {
func Check(reg *docs.Registry, strict bool, w io.Writer) int {
errs, warns := 0, 0
for _, p := range append(reg.Check(strict), extra...) {
for _, p := range reg.Check(strict) {
level := "warn "
if p.Error {
level = "error"
+26 -93
View File
File diff suppressed because it is too large Load Diff
+2 -6
View File
@@ -21,11 +21,7 @@ func Tool(p *project.Project, args []string, stdout, stderr io.Writer, mods ...m
if err := fs.Parse(args[1:]); err != nil {
return 2
}
build := Registry
if args[0] == "export" {
build = ExportRegistry
}
reg, err := build(p, mods)
reg, err := Registry(p, mods)
if err != nil {
fmt.Fprintln(stderr, "aego:", err)
return 1
@@ -38,7 +34,7 @@ func Tool(p *project.Project, args []string, stdout, stderr io.Writer, mods ...m
}
return 0
case "check":
return Check(reg, *strict, stdout, DiagnosticProblems()...)
return Check(reg, *strict, stdout)
}
fmt.Fprintf(stderr, "unknown docs command %q\n", args[0])
return 2
-77
View File
@@ -1,77 +0,0 @@
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)
}
var code string
var err error
if e.TryScript != "" {
code, err = pg.TryCode(e.TryScript)
}
switch res := pg.PlayScene(e.ID, []byte(e.Try), code, e.TryPrefabs); {
case !sandbox[e.ID]:
add("belongs to a module the docs runtime does not include")
case err != nil:
add("script does not compile: " + err.Error())
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
}
-420
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-96
View File
@@ -1,96 +0,0 @@
//go:build aego_editor
package docsview
import (
"encoding/json"
"strconv"
playmods "aego/app/assembly/playground"
"aego/editor/core/docsession"
"aego/scene/sceneio"
)
type request struct {
Scene string `json:"scene"`
Select string `json:"select"`
Code string `json:"code"`
Prefabs map[string]string `json:"prefabs"`
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":
mods, derr := playmods.DecodeTry(req.Code)
if err = derr; err == nil {
err = a.scripts.use(mods)
}
if err == nil {
err = playmods.MountTry(a.loader, req.Prefabs)
}
if err == nil {
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)
}
-79
View File
@@ -1,79 +0,0 @@
//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
}
// use makes mods the scripts of the next scene and publishes their types, so the scene keeps their fields.
func (h *scriptHost) use(mods []*bytecode.Module) error {
if err := scriptruntime.Publish(h.types.Schema(), mods, h.reg.Surface()); err != nil {
return err
}
h.mods = mods
return nil
}
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)
-27
View File
@@ -1,27 +0,0 @@
package edit
import (
"os"
"os/exec"
"runtime"
"strings"
"aego/engine"
"aego/platform"
)
func startBoot(verb string) {
wd, _ := os.Getwd()
exe, _ := os.Executable()
gobin, err := exec.LookPath("go")
if err != nil {
gobin = "not on PATH"
}
_ = platform.StartBoot(platform.DefaultBootPath(),
"aego "+engine.Version+" "+runtime.GOOS+"/"+runtime.GOARCH+" "+verb,
"exe "+exe,
"args "+strings.Join(os.Args[1:], " "),
"cwd "+wd,
"go "+gobin,
)
}
+48 -15
View File
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -116,8 +116,12 @@ func TestPlayLeavesEditSceneByteIdentical(t *testing.T) {
t.Fatal("Spin is not registered")
}
speed, _ := td.Field("speed")
add := commands.NewMutation("Add "+td.FullName, commands.EnvFor(es),
commands.AddComp(es, spinner, td, []commands.FieldValue{{Field: speed.ID, Value: schema.Float32(3)}}))
add := &commands.AddComponent{
Node: spinner,
Type: td.ID,
Name: td.FullName,
Values: []commands.FieldValue{{Field: speed.ID, Value: schema.Float32(3)}},
}
if err := d.History.Exec(add); err != nil {
t.Fatal(err)
}
-163
View File
File diff suppressed because it is too large Load Diff
-49
View File
@@ -1,19 +1,14 @@
package edit
import (
"net"
"net/http"
"os/exec"
"path/filepath"
"runtime"
"sync"
"aego/app/bootstrap"
"aego/app/export"
"aego/app/projects"
"aego/asset/pipeline"
"aego/editor/core/services"
"aego/editor/core/sysopen"
"aego/engine/problems"
"aego/project"
)
@@ -92,47 +87,3 @@ func (x *exporter) Reveal(path string) error {
}
return cmd.Start()
}
var webServers sync.Map
func serveWeb(dir string) (string, error) {
if u, ok := webServers.Load(dir); ok {
return u.(string), nil
}
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return "", err
}
files := http.FileServer(http.Dir(dir))
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cross-Origin-Opener-Policy", "same-origin")
w.Header().Set("Cross-Origin-Embedder-Policy", "require-corp")
w.Header().Set("Cache-Control", "no-store")
files.ServeHTTP(w, r)
})
go http.Serve(ln, h)
u := "http://" + ln.Addr().String() + "/"
webServers.Store(dir, u)
return u, nil
}
func (x *exporter) Run(out, target string) error {
path, err := export.Launchable(x.p, target, out)
if err != nil {
return err
}
t, _ := export.ParseTarget(target)
switch {
case t.Web():
u, err := serveWeb(path)
if err != nil {
return err
}
return sysopen.Open(u)
case t.OS == "darwin":
return exec.Command("open", path).Start()
}
cmd := exec.Command(path)
cmd.Dir = filepath.Dir(path)
return cmd.Start()
}

Some files were not shown because too many files have changed in this diff Show More