Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cc0673082 | ||
|
|
ab9b003457 | ||
|
|
41d949717d | ||
|
|
69d19e501f | ||
|
|
cf68c71f05 | ||
|
|
08f0b15e1e | ||
|
|
17cb5e4291 | ||
|
|
eb9ac1e589 | ||
|
|
dae4bc19e1 |
@@ -5,4 +5,3 @@ site
|
|||||||
exports
|
exports
|
||||||
**/.aego
|
**/.aego
|
||||||
*.test
|
*.test
|
||||||
.claude
|
|
||||||
|
|||||||
@@ -94,14 +94,4 @@ jobs:
|
|||||||
- name: example project
|
- name: example project
|
||||||
run: |
|
run: |
|
||||||
/tmp/aego check examples/native-rotator
|
/tmp/aego check examples/native-rotator
|
||||||
/tmp/aego run examples/native-rotator --headless --frames 60
|
/tmp/aego run examples/native-rotator --headless --frames 60
|
||||||
- name: export packages
|
|
||||||
run: go test -count=1 ./runtime/... ./vfs/exportfs ./app/export/...
|
|
||||||
- name: export and run headless
|
|
||||||
run: go test -count=1 -run 'TestExport' -v ./tests/integration
|
|
||||||
- name: editor app bundle
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: |
|
|
||||||
make editor-app
|
|
||||||
plutil -lint ".cache/Aego Editor.app/Contents/Info.plist"
|
|
||||||
codesign --verify --deep ".cache/Aego Editor.app"
|
|
||||||
@@ -3,7 +3,6 @@ name: ci
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
tags: ["v*"]
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -37,10 +36,6 @@ jobs:
|
|||||||
- run: go vet ./platform/sdl
|
- run: go vet ./platform/sdl
|
||||||
- run: make lint
|
- run: make lint
|
||||||
- run: make depcheck
|
- run: make depcheck
|
||||||
- name: packaging tools
|
|
||||||
run: sudo apt-get install -y nsis squashfs-tools
|
|
||||||
- name: installer dry run
|
|
||||||
run: make nsis-check
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
@@ -116,39 +111,4 @@ jobs:
|
|||||||
- name: example project
|
- name: example project
|
||||||
run: |
|
run: |
|
||||||
/tmp/aego check examples/native-rotator
|
/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,10 +11,4 @@ tools/genicons/genicons
|
|||||||
/aego
|
/aego
|
||||||
/aego-editor
|
/aego-editor
|
||||||
/aego-playground
|
/aego-playground
|
||||||
/aego-runtime
|
|
||||||
*.syso
|
|
||||||
*.test
|
*.test
|
||||||
|
|
||||||
/tests/golden/testdata/actual-*
|
|
||||||
tools/websmoke/node_modules/
|
|
||||||
tools/websmoke/golden/actual-*.png
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
# Aego
|
|
||||||
|
|
||||||
A 2D and 3D game engine with an editor, a scripting language and a web export, written in Go.
|
|
||||||
|
|
||||||
- Documentation and the playground: <https://aego.akarpov.ru>
|
|
||||||
- Installing and building from source: [engine/res/docs/install.md](engine/res/docs/install.md)
|
|
||||||
- Contributing, check levels and rules for engine code: [docs/contributing.md](docs/contributing.md)
|
|
||||||
- Architecture and decisions: [ARCHITECTURE.md](ARCHITECTURE.md)
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"aego/core/log"
|
"aego/core/log"
|
||||||
"aego/engine/base"
|
"aego/engine/base"
|
||||||
"aego/engine/module"
|
"aego/engine/module"
|
||||||
|
"aego/platform/input"
|
||||||
"aego/scene"
|
"aego/scene"
|
||||||
"aego/script/bind"
|
"aego/script/bind"
|
||||||
)
|
)
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
type Options struct {
|
type Options struct {
|
||||||
Seed uint64
|
Seed uint64
|
||||||
Testing bool
|
Testing bool
|
||||||
|
Input *input.AxisConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
|
|||||||
@@ -4,22 +4,10 @@ import (
|
|||||||
"aego/app/assembly"
|
"aego/app/assembly"
|
||||||
"aego/engine/base"
|
"aego/engine/base"
|
||||||
"aego/engine/module"
|
"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/physics"
|
||||||
"aego/modules/render2d"
|
"aego/modules/render2d"
|
||||||
"aego/modules/render3d"
|
"aego/modules/render3d"
|
||||||
"aego/modules/savegame"
|
|
||||||
"aego/modules/scripting"
|
"aego/modules/scripting"
|
||||||
settingsmod "aego/modules/settings"
|
|
||||||
"aego/modules/spawn"
|
|
||||||
"aego/modules/tween"
|
|
||||||
"aego/script/stdlib"
|
"aego/script/stdlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,10 +15,6 @@ func core() assembly.Entry {
|
|||||||
return assembly.Entry{ID: base.CoreID, New: func(assembly.Options) module.Module { return base.Core{} }}
|
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 {
|
func render2dEntry() assembly.Entry {
|
||||||
return assembly.Entry{ID: render2d.ID, New: func(assembly.Options) module.Module { return render2d.New() }}
|
return assembly.Entry{ID: render2d.ID, New: func(assembly.Options) module.Module { return render2d.New() }}
|
||||||
}
|
}
|
||||||
@@ -41,7 +25,7 @@ func render3dEntry() assembly.Entry {
|
|||||||
|
|
||||||
func scriptEntry() assembly.Entry {
|
func scriptEntry() assembly.Entry {
|
||||||
return assembly.Entry{ID: scripting.ID, New: func(o assembly.Options) module.Module {
|
return assembly.Entry{ID: scripting.ID, New: func(o assembly.Options) module.Module {
|
||||||
return scripting.New(stdlib.Options{Seed: o.Seed, Testing: o.Testing})
|
return scripting.New(stdlib.Options{Seed: o.Seed, Testing: o.Testing, Input: o.Input})
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,51 +33,6 @@ func physicsEntry() assembly.Entry {
|
|||||||
return assembly.Entry{ID: physics.ID, New: func(assembly.Options) module.Module { return physics.New() }}
|
return assembly.Entry{ID: physics.ID, New: func(assembly.Options) module.Module { return physics.New() }}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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() }}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
func Default() assembly.Catalog {
|
||||||
return assembly.Catalog{core(), dataEntry(), inputEntry(), render2dEntry(), render3dEntry(), scriptEntry(), physicsEntry(), debugEntry(),
|
return assembly.Catalog{core(), render2dEntry(), render3dEntry(), scriptEntry(), physicsEntry()}
|
||||||
displayEntry(), audioEntry(), accessibilityEntry(), settingsEntry(), tweenEntry(), cameraEntry(), gameplayEntry(), spawnEntry(), savegameEntry()}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package builtin
|
|||||||
import "aego/app/assembly"
|
import "aego/app/assembly"
|
||||||
|
|
||||||
func Headless() assembly.Catalog {
|
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(), scriptEntry(), physicsEntry()}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package builtin
|
|
||||||
|
|
||||||
import "aego/app/assembly"
|
|
||||||
|
|
||||||
func Runtime() assembly.Catalog {
|
|
||||||
return Default()
|
|
||||||
}
|
|
||||||
@@ -24,10 +24,6 @@ func Manifest(user ...module.Module) (*manifest.Manifest, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AuditIcons(user ...module.Module) (*module.IconReport, 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()
|
types, err := assembly.NewTypes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -36,7 +32,7 @@ func AuditIconsWith(extra []module.IconUse, user ...module.Module) (*module.Icon
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return module.AuditUses(extra, types, bind.NewManifest(), append(mods, user...)...)
|
return module.Audit(types, bind.NewManifest(), append(mods, user...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func IconSources() []string {
|
func IconSources() []string {
|
||||||
|
|||||||
@@ -2,16 +2,6 @@ package editors
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"aego/app/assembly"
|
"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"
|
|
||||||
"aego/modules/gameplay"
|
|
||||||
gameplayeditor "aego/modules/gameplay/editor"
|
|
||||||
inputmod "aego/modules/input"
|
|
||||||
inputeditor "aego/modules/input/editor"
|
|
||||||
"aego/modules/physics"
|
"aego/modules/physics"
|
||||||
physicseditor "aego/modules/physics/editor"
|
physicseditor "aego/modules/physics/editor"
|
||||||
"aego/modules/render2d"
|
"aego/modules/render2d"
|
||||||
@@ -22,12 +12,7 @@ import (
|
|||||||
|
|
||||||
func With(c assembly.Catalog) assembly.Catalog {
|
func With(c assembly.Catalog) assembly.Catalog {
|
||||||
return c.
|
return c.
|
||||||
With(inputmod.ID, inputeditor.Hook).
|
|
||||||
With(render2d.ID, render2deditor.Hook).
|
With(render2d.ID, render2deditor.Hook).
|
||||||
With(render3d.ID, render3deditor.Hook).
|
With(render3d.ID, render3deditor.Hook).
|
||||||
With(physics.ID, physicseditor.Hook).
|
With(physics.ID, physicseditor.Hook)
|
||||||
With(camera.ID, cameraeditor.Hook).
|
|
||||||
With(gameplay.ID, gameplayeditor.Hook).
|
|
||||||
With(audio.ID, audioeditor.Hook).
|
|
||||||
With(debug.ID, debugeditor.Hook)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,14 +44,3 @@ func (h hooked) Shutdown() {
|
|||||||
sd.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,48 +4,17 @@ import (
|
|||||||
"aego/app/assembly"
|
"aego/app/assembly"
|
||||||
"aego/engine/base"
|
"aego/engine/base"
|
||||||
"aego/engine/module"
|
"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/render2d"
|
||||||
"aego/modules/render3d"
|
|
||||||
"aego/modules/savegame"
|
|
||||||
"aego/modules/scripting"
|
"aego/modules/scripting"
|
||||||
"aego/modules/spawn"
|
|
||||||
"aego/modules/tween"
|
|
||||||
"aego/script/stdlib"
|
"aego/script/stdlib"
|
||||||
"aego/script/vm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Catalog() assembly.Catalog {
|
func Catalog() assembly.Catalog {
|
||||||
return assembly.Catalog{
|
return assembly.Catalog{
|
||||||
{ID: base.CoreID, New: func(assembly.Options) module.Module { return base.Core{} }},
|
{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: render2d.ID, New: func(assembly.Options) module.Module { return render2d.New() }},
|
||||||
{ID: scripting.ID, New: func(o assembly.Options) module.Module {
|
{ID: scripting.ID, New: func(o assembly.Options) module.Module {
|
||||||
return scripting.New(stdlib.Options{Seed: o.Seed, Testing: o.Testing})
|
return scripting.New(stdlib.Options{Seed: o.Seed, Testing: o.Testing, Input: o.Input})
|
||||||
}},
|
}},
|
||||||
{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}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"aego/app/proc"
|
|
||||||
"aego/core/errs"
|
"aego/core/errs"
|
||||||
"aego/core/log"
|
"aego/core/log"
|
||||||
"aego/project"
|
"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 := exec.Command("go", args...)
|
||||||
cmd.Dir = p.Root
|
cmd.Dir = p.Root
|
||||||
cmd.Env = append(os.Environ(), "GOOS="+t.OS, "GOARCH="+t.Arch)
|
cmd.Env = append(os.Environ(), "GOOS="+t.OS, "GOARCH="+t.Arch)
|
||||||
proc.Hide(cmd)
|
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.RemoveAll(outDir)
|
os.RemoveAll(outDir)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"aego/app/proc"
|
|
||||||
"aego/core/errs"
|
"aego/core/errs"
|
||||||
"aego/core/log"
|
"aego/core/log"
|
||||||
"aego/project"
|
"aego/project"
|
||||||
@@ -33,14 +32,12 @@ func RunIcons(p *project.Project, engineVersion string, out io.Writer) (int, err
|
|||||||
defer os.Remove(bin)
|
defer os.Remove(bin)
|
||||||
build := exec.Command("go", "build", "-tags", strings.Join(t.Tags, ","), "-o", bin, "./.aego/generated/icons")
|
build := exec.Command("go", "build", "-tags", strings.Join(t.Tags, ","), "-o", bin, "./.aego/generated/icons")
|
||||||
build.Dir = p.Root
|
build.Dir = p.Root
|
||||||
proc.Hide(build)
|
|
||||||
if output, err := build.CombinedOutput(); err != nil {
|
if output, err := build.CombinedOutput(); err != nil {
|
||||||
return 2, errs.New(errs.BuildFailed, "native icons helper does not compile",
|
return 2, errs.New(errs.BuildFailed, "native icons helper does not compile",
|
||||||
log.F("output", strings.TrimSpace(string(output))))
|
log.F("output", strings.TrimSpace(string(output))))
|
||||||
}
|
}
|
||||||
run := exec.Command(bin)
|
run := exec.Command(bin)
|
||||||
run.Dir, run.Stdout, run.Stderr = p.Root, out, out
|
run.Dir, run.Stdout, run.Stderr = p.Root, out, out
|
||||||
proc.Hide(run)
|
|
||||||
err = run.Run()
|
err = run.Run()
|
||||||
var ee *exec.ExitError
|
var ee *exec.ExitError
|
||||||
if errors.As(err, &ee) {
|
if errors.As(err, &ee) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"aego/app/proc"
|
|
||||||
"aego/core/errs"
|
"aego/core/errs"
|
||||||
"aego/core/log"
|
"aego/core/log"
|
||||||
"aego/project"
|
"aego/project"
|
||||||
@@ -53,7 +52,6 @@ func BuildManifest(p *project.Project, engineVersion string, lg *log.Logger) (st
|
|||||||
defer os.Remove(bin)
|
defer os.Remove(bin)
|
||||||
build := exec.Command("go", "build", "-tags", strings.Join(t.Tags, ","), "-o", bin, "./.aego/generated/manifest")
|
build := exec.Command("go", "build", "-tags", strings.Join(t.Tags, ","), "-o", bin, "./.aego/generated/manifest")
|
||||||
build.Dir = p.Root
|
build.Dir = p.Root
|
||||||
proc.Hide(build)
|
|
||||||
if output, err := build.CombinedOutput(); err != nil {
|
if output, err := build.CombinedOutput(); err != nil {
|
||||||
return "", errs.New(errs.BuildFailed, "native bindings helper does not compile",
|
return "", errs.New(errs.BuildFailed, "native bindings helper does not compile",
|
||||||
log.F("output", strings.TrimSpace(string(output))))
|
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"
|
tmp := out + ".tmp"
|
||||||
run := exec.Command(bin, tmp)
|
run := exec.Command(bin, tmp)
|
||||||
run.Dir = p.Root
|
run.Dir = p.Root
|
||||||
proc.Hide(run)
|
|
||||||
if output, err := run.CombinedOutput(); err != nil {
|
if output, err := run.CombinedOutput(); err != nil {
|
||||||
os.Remove(tmp)
|
os.Remove(tmp)
|
||||||
return "", errs.New(errs.BuildFailed, "native module Register failed",
|
return "", errs.New(errs.BuildFailed, "native module Register failed",
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 878 B |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,27 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -138,7 +138,7 @@ func TestF1Refs(t *testing.T) {
|
|||||||
if err := os.WriteFile(path, []byte(probe), 0o644); err != nil {
|
if err := os.WriteFile(path, []byte(probe), 0o644); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
reg, ws, err := build(p, nil, nil)
|
reg, ws, err := build(p, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package docstool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"aego/app/playground"
|
|
||||||
"aego/engine/docs"
|
|
||||||
"aego/engine/module"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CheckExamples loads the example scene of every module code, validates it and runs it headless; the code must be among the problems it reports (D146).
|
|
||||||
func CheckExamples(reg *docs.Registry, w io.Writer) int {
|
|
||||||
pg, err := playground.New()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(w, "examples: 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()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
bad := 0
|
|
||||||
for _, e := range reg.All() {
|
|
||||||
if e.Kind != docs.Code || e.Diag == nil || e.Diag.Example == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found, why := false, ""
|
|
||||||
code := ""
|
|
||||||
if e.Diag.ExampleScript != "" {
|
|
||||||
c, err := pg.TryCode(e.Diag.ExampleScript)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(w, "%s: error: example script does not compile: %v\n", e.ID, err)
|
|
||||||
bad++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
code = c
|
|
||||||
}
|
|
||||||
if res := pg.PlayScene("example-"+e.ID, []byte(e.Diag.Example), code, nil); !res.Started {
|
|
||||||
why = "does not load: " + res.Error
|
|
||||||
} else {
|
|
||||||
for _, d := range module.ValidateScene(pg.Game().Scene(), nil) {
|
|
||||||
found = found || d.Code == e.ID
|
|
||||||
}
|
|
||||||
if err := pg.Game().Step(tryFrames); err != nil {
|
|
||||||
why = "stops the engine: " + err.Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
poll := pg.Poll()
|
|
||||||
for _, f := range poll.Faults {
|
|
||||||
found = found || f.Code == e.ID
|
|
||||||
}
|
|
||||||
for _, l := range poll.Log {
|
|
||||||
found = found || strings.Contains(l.Text, e.ID)
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case why != "":
|
|
||||||
fmt.Fprintf(w, "%s: error: example %s %s\n", e.ID, e.Diag.ExampleFile, why)
|
|
||||||
bad++
|
|
||||||
case !found:
|
|
||||||
fmt.Fprintf(w, "%s: error: example of %s does not produce %s\n", e.ID, e.ID, e.ID)
|
|
||||||
bad++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pg.Stop()
|
|
||||||
return bad
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"aego/app/assembly"
|
"aego/app/assembly"
|
||||||
"aego/app/assembly/builtin"
|
"aego/app/assembly/builtin"
|
||||||
"aego/app/playground"
|
|
||||||
"aego/engine/docs"
|
"aego/engine/docs"
|
||||||
"aego/engine/module"
|
"aego/engine/module"
|
||||||
scriptsystems "aego/modules/scripting/systems"
|
scriptsystems "aego/modules/scripting/systems"
|
||||||
@@ -21,8 +20,7 @@ import (
|
|||||||
"aego/script/workspace"
|
"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) (*docs.Registry, *workspace.Workspace, error) {
|
||||||
func build(p *project.Project, mods []module.Module, pg *playground.Playground) (*docs.Registry, *workspace.Workspace, error) {
|
|
||||||
types, err := assembly.NewTypes()
|
types, err := assembly.NewTypes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -43,10 +41,6 @@ func build(p *project.Project, mods []module.Module, pg *playground.Playground)
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
src := docs.SourcesOf(bm, types.Schema(), man)
|
src := docs.SourcesOf(bm, types.Schema(), man)
|
||||||
if pg != nil {
|
|
||||||
src.Diagnose = diagnoser(pg)
|
|
||||||
src.CompileTry = pg.TryCode
|
|
||||||
}
|
|
||||||
var ws *workspace.Workspace
|
var ws *workspace.Workspace
|
||||||
if p != nil {
|
if p != nil {
|
||||||
ws, err = workspace.Open(workspace.Config{Root: p.Root, Module: p.Module, Scripts: p.ScriptsDir(), Bindings: reg.Table(), Docs: bm})
|
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) {
|
func Registry(p *project.Project, mods []module.Module) (*docs.Registry, error) {
|
||||||
r, _, err := build(p, mods, nil)
|
r, _, err := build(p, mods)
|
||||||
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)
|
|
||||||
return r, err
|
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)
|
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
|
errs, warns := 0, 0
|
||||||
for _, p := range append(reg.Check(strict), extra...) {
|
for _, p := range reg.Check(strict) {
|
||||||
level := "warn "
|
level := "warn "
|
||||||
if p.Error {
|
if p.Error {
|
||||||
level = "error"
|
level = "error"
|
||||||
|
|||||||