27 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
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
sanspie ab9b003457 Stage 8: physics and module boundaries 2026-10-15 19:16:09 +03:00
sanspie 41d949717d deploy: push-to-deploy docs site and playground
make site-headless builds the CLI without cgo (aego_nonative) and the
static site with the wasm playground. deploy/ holds the image, the
container nginx config (gzip_static with Vary), compose and deploy.sh
run by the post-receive hook on mainserv.

.gitignore ignores only the root site/ output; tools/gendocs/site was
never committed because of it.

Docs site: index and section summaries render code, bold and italic
instead of raw markdown; search.json gets the plain text. Page summaries
drop a leading list or quote marker. The nav toggle is three CSS bars
centred in a 32px button.

Playground: a loading overlay in the editor pane shows a spinner, a
progress bar and the downloaded size while aego.wasm streams in, then
'Starting engine' until the runtime is ready; a failed start keeps the
error on the overlay. docstool writes the wasm size and a version hash
into index.html; play.js, play.css, wasm_exec.js and aego.wasm are
referenced with ?v=<hash> so a deploy never mixes cached old files with
new ones.

Instruction budget: default 1M per callback (was 100k), playground 2M;
the donut needs ~120k per frame. Playground: software 3D donut example,
multi-line log entries render as monospace blocks, a fault is reported
once instead of also as a "script stopped" log line.

The compose project is named aego explicitly; the default name came from
the deploy/ directory and collided with other stacks on the host.
2026-10-14 22:40:20 +03:00
sanspie 69d19e501f Stage 7: documentation, help, playground; templates, aego new/bootstrap/icons, launcher
Documentation: engine/docs assembles one registry from the script
bindings, the schema, module docs and module-owned script paths; problems
are reported as warnings at build time. Module registries record doc
sources and script module owners; diag.Codes lists every diagnostic code.

Project bootstrap: project.defaultSpace ("2d"/"3d"), EnsureDirs creates
the default folders when the editor opens a project (reported in the
console), prefabs/ is a pipeline root. Native builds drop -trimpath so
binding locations resolve inside native/.

Editor files: create scene/prefab/script from menus, move/rename/duplicate
/delete with undo, script package retargeting on move, open scenes and
script panels follow renames, Save As / Rename Scene prompts, scene
picker, startup scene. Scoped shortcuts fall back to the global command
when disabled; Save As is Ctrl+Shift+S, Save All Ctrl+Alt+S.

Script editor: Go to Implementation (stub → native/ source), host
definitions always resolve to stubs, bracket match/mismatch states,
rainbow brackets (persisted), mono code font (ui-mono atlases, per-command
fonts in draw.List, PushFont/PopFont restored by Mark/Restore).

Tests: langsvc definition/implementation, bracket states, dispatch
fallback, script renames, font stack, editor file-op integration, golden
mono glyph and bracket references; depcheck rule for engine/docs.

Docs pipeline: engine, render2d and script/engine ship their own res/docs;
keyword hover text comes from the registry through manifest.Keywords;
project script symbols join the registry; aego docs export|check|build|
serve, --tool docs in native binaries, aego check --explain; app/docstool
and the tools/gendocs module (goldmark) build the static site.

Playground: app/playground + cmd/aego-playground (js/wasm, 3.3 MB gzip)
compile, highlight and run scripts in the browser; run blocks in the
language docs become examples. engine/builtin/headless is the module set
without render3d; workspace.Config.FS; StubFor/StubPaths; backends and the
script importer drop out of js builds.

VM: one frame pointer in the interpreter loop, direct host calls, panic
recovery once per run instead of per host call (Update2k -19%,
Schedule10k -24% against the previous head). bench-build/compare/profile
run pre-built, pinned, interleaved binaries.

Editor: structural bracket levels and current-block highlight, indent
guides, occurrence highlighting with write marks, multi-format clipboard
(copy/cut/paste assets, paste images as sprites or textures). gl33 probes
a renderable depth format and reports framebuffer failures by stage.

7 E:
- project templates empty/2d/2.5d/3d, app/projects as the single creation path (D148)
- launcher and recent projects (D149)
- orthographic Camera3D and Scene View (D150)
- reference states in the Inspector, ids.Short (D151)
- sceneio remaps node references when loading without KeepIDs (D152)
- exported ComponentRef<T> (D153)
- aego icons via module.Audit (D146)
2026-10-13 01:08:56 +03:00
sanspie cf68c71f05 Stage 6: coroutines, signals, ScriptRef, workspace CLI, editor tooling
Language: yield/signal/coroutine become keywords, start is contextual.
Coroutine methods (func Name() coroutine) with start and
yield / wait / frames / fixed / await; signals with Emit/Connect and
Connection handles; ScriptRef<T> and node.GetScript<T>() for fields,
plain methods and signals of other scripts. Diagnostics AES2050-AES2059.

Bytecode v3: ScriptRefs, XFields, XMethods and Connects tables resolved by
name in vm.Link, per-script signals, coroutine flag on functions; new
opcodes START/YIELD/EMIT/CONNECT/DISCONNECT/CONNECTED/COSTOP/CORUN/
SELFREF/GETSCRIPT/GETXF/SETXF/CALLR, checked by verify.

Runtime: script/coroutine scheduler (frames/fixed counters, time heap,
await lists; only due coroutines are visited) and script/signal registry
(snapshot per Emit, deferred removal, no allocations in steady state).
Hot reload cancels coroutines of changed scripts, rebinds connections by
name and signature, then runs Reloaded.

Tooling: script/build removed, aego check/test/dump run on the workspace
(test builds include _test.aes); langsvc completion, hover, signatures and
semantic tokens for the new syntax; editor highlighting, snippets with
placeholders, coroutine/signal profiler graphs and stats.

Docs (script.md sections 16-19, editor.md, ARCHITECTURE), test fixtures and
golden files, platformer enemies wired to the player, scriptreload demo,
and run.Config.FrameTime for deterministic headless fixed steps.

Modules (6.5): engine/module registry and ComponentBuilder are the one
registration path for engine subsystems and native code; render2d,
render3d and script/engine are modules over scene/frame, the script
component moved to scene/scriptcomp, and scene/components and
scene/systems are gone. Editor inspector, templates, picking shapes and
extension hooks are driven by descriptors and editor/builtins; sdk gains
Component/EditorHook; depcheck checks the module graph and the
aego_runtime graph.

Also: stop tracking built tool binaries; gl33 activates the scratch unit
before binding for texture updates.

Verification: typed component method adapters (Method0..2, Proc0..2)
with direct field access and no allocations; app.Build as the single
construction path for run and the editor; modules attach per scene
document; depcheck aego_editor pass; diagnostic codes in one place and
every code covered by a test; indexed coroutine heaps with eager
cancel; dead component references and nil intrinsics fault with
AES6004, host faults keep their code; GC roots of struct fields checked
in verify and vm.Link; 255-field limit; AES4010 for script packages
shadowing host modules; architecture registry D111-D147.

Golden: the harness renders on the thread that owns the GL context,
ReadPixels returns rows in memory order (render targets are top-down per
D17), and the first rendering references are committed.
2026-10-12 14:22:08 +03:00
sanspie 08f0b15e1e docs: update architecture spec and script reference 2026-10-08 13:31:21 +03:00
sanspie 17cb5e4291 Stage 2-5: assets, scene/ECS, editor, 3D, and Aego Script
Stage 2: render pipeline and UI toolkit over gpu/platform.
Stage 3: schema registry, asset pipeline + runtime loader, scene graph and ECS.
Stage 4: editor — docking, inspector, scene/game views, play mode, asset browser, console, profiler; 4.5 adds the 3D foundation (mixed 2D/3D spaces, meshes, depth).
Stage 5: Aego Script — compiler (lexer/parser/types/resolver/checker/asm/compiler), bytecode with verifier and register VM, instance runtime, stdlib, native bindings, and editor/CLI integration (aego run/edit/check/test/dump).
2026-10-08 13:17:28 +03:00
sanspie eb9ac1e589 Initial commit 2026-09-26 16:32:40 +03:00
sanspie dae4bc19e1 Stage 1: engine core, platform, gpu, vfs
Foundation for the aego engine.

core/    errs, log, handle arena, mathx, clock, stats
platform SDL3 backend plus a headless one, input state, events
gpu      device abstraction with gl33 and null backends
vfs      scheme-based virtual filesystem with overlay mounts
engine   builder, module registry, phase-ordered system loop, jobs
demos    basic, resize, input, headless; sandbox driver

OpenGL bindings are generated into gpu/gl33/gl by tools/gen-gl.sh,
so the module has no external dependencies.
2026-09-18 20:51:54 +03:00
809 changed files with 3229 additions and 62634 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
+30 -253
View File
File diff suppressed because one or more lines are too long
+9 -108
View File
File diff suppressed because it is too large Load Diff
-8
View File
@@ -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)
-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
-439
View File
File diff suppressed because it is too large Load Diff
-222
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()}
}
-9
View File
@@ -2,14 +2,8 @@ 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"
"aego/modules/gameplay"
gameplayeditor "aego/modules/gameplay/editor"
inputmod "aego/modules/input"
inputeditor "aego/modules/input/editor"
"aego/modules/physics"
@@ -26,8 +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(gameplay.ID, gameplayeditor.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

+3 -5
View File
@@ -41,7 +41,6 @@ type Config struct {
Editor bool
FailOnScriptErrors bool
Catalog assembly.Catalog
Platform platform.Platform
Profile string
NoProfile bool
}
@@ -99,9 +98,6 @@ func (h *Host) build(cfg Config, mods []module.Module) error {
ecfg.LoopScope = cfg.LoopScope
}
builder := engine.NewBuilder(ecfg).WithLogger(lg).Use(rmod)
if cfg.Platform != nil {
builder.WithPlatform(cfg.Platform)
}
if h.Types, err = assembly.NewTypes(); err != nil {
return err
}
@@ -191,7 +187,6 @@ func (h *Host) build(cfg Config, mods []module.Module) error {
}
h.Settings.SetNames(h.Sections.Names)
h.Engine.SetService(settings.ServiceID, h.Settings)
h.Engine.OnClose(h.Modules.Shutdown)
if err = h.Modules.Init(&module.InitContext{Engine: h.Engine, Assets: h.Loader, Log: lg, Editor: cfg.Editor, Sections: h.Sections, Problems: h.Problems, Settings: h.Settings}); err != nil {
return err
}
@@ -232,6 +227,9 @@ func (h *Host) Close() {
return
}
h.closed = true
if h.Modules != nil {
h.Modules.Shutdown()
}
if h.Engine != nil {
h.Engine.Close()
}
-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)
}
}
-70
View File
@@ -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
}
+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 -96
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
File diff suppressed because it is too large Load Diff
-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

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