Files
aego/modules/update/update_native.go
sanspie 962c479933
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
ci / checks (push) Successful in 10m24s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 12m13s
ci / release (push) Successful in 8m57s
web: the browser build drops the self-update core (D362 amended)
modules/update links aego/update (net/http, TLS, x509, archives) only off js; the web build gets a browser backend with the same section, settings and aego/update API, where Check and Apply fail and State stays idle. A test compares both builds' registrations.
depcheck: wasm-runtime and wasm-try variants, update in wasmDenied, wasmDeniedStd (net/http, crypto/tls, crypto/x509, archive/*) with the wasm-reaches-std rule.
wasmsize -prev: playground-size and try-size print the change since their last run.
Loaders show the compressed download size: wasmGzip in an export's files.json, aego-*-gzip and data-wasm-gzip on the site; docstool writes the .wasm.gz copies.
Demo game.wasm 33.9 -> 26.0 MB, gzip 8.07 -> 6.25 MB.
2026-10-27 20:06:16 +03:00

143 lines
2.9 KiB
Go

//go:build !js
package update
import (
"context"
"errors"
"os"
"os/exec"
"path/filepath"
"runtime"
"aego/engine/module"
upd "aego/update"
)
func newBackend() backend {
return &native{}
}
// native updates the game's own install from its release server.
type native struct {
rel *upd.Release
file string
in upd.Install
}
func (n *native) init(m *Module, ctx *module.InitContext) error {
descs := Settings()
var err error
if m.mode, err = ctx.Setting(descs[0]); err != nil {
return err
}
if m.ch, err = ctx.Setting(descs[1]); err != nil {
return err
}
if exe, err := os.Executable(); err == nil {
n.in = upd.DetectGame(exe)
}
upd.Clean(downloads(m), m.ver)
if policy(m) != upd.Off {
n.check(m)
}
return nil
}
func policy(m *Module) upd.Mode {
if m.st == nil || m.mode == 0 {
return upd.Auto
}
return upd.ParseMode(m.st.ChoiceName(m.mode))
}
func channel(m *Module) upd.Channel {
if m.st == nil || m.ch == 0 {
return upd.Stable
}
return upd.ParseChannel(m.st.ChoiceName(m.ch))
}
// downloads is where the game's updates are fetched to: UserCacheDir/<game>/updates.
func downloads(m *Module) string {
dir, _ := os.UserCacheDir()
return filepath.Join(dir, m.game, "updates")
}
func (n *native) check(m *Module) error {
m.mu.Lock()
if !m.Configured() || m.phase == Checking || m.phase == Downloading {
m.mu.Unlock()
return nil
}
m.phase, m.err = Checking, ""
src := &upd.Source{BaseURL: m.sec.URL, Channel: channel(m), Key: m.sec.PublicKey}
m.mu.Unlock()
go func() {
r, err := src.Check(context.Background())
m.mu.Lock()
switch {
case err != nil:
m.phase, m.err = Failed, err.Error()
case !r.Newer(upd.Build{Version: m.ver}):
m.phase = UpToDate
default:
m.phase, n.rel, m.newVer, m.notes = Available, r, r.Version, r.Notes
}
auto := m.phase == Available && policy(m) == upd.Auto
m.mu.Unlock()
if auto {
n.download(m)
}
}()
return nil
}
func (n *native) download(m *Module) {
m.mu.Lock()
r := n.rel
if r == nil || m.phase == Downloading {
m.mu.Unlock()
return
}
m.phase = Downloading
m.mu.Unlock()
go func() {
kind := upd.KindTarball
if n.in.Kind == upd.KindAppImage || n.in.Kind == upd.KindApp {
kind = n.in.Kind
}
var file string
a, err := r.Asset(runtime.GOOS, runtime.GOARCH, kind)
if err == nil {
file, err = upd.Download(context.Background(), nil, a, filepath.Join(downloads(m), r.Version), nil)
}
m.mu.Lock()
defer m.mu.Unlock()
if err != nil {
m.phase, m.err = Failed, err.Error()
return
}
m.phase, n.file = Ready, file
}()
}
func (n *native) apply(m *Module) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.phase != Ready || n.rel == nil {
return errors.New("no update is downloaded")
}
if _, err := n.in.Apply(n.file, n.rel.Version); err != nil {
return err
}
cmd := exec.Command(n.in.Launch, os.Args[1:]...)
if err := cmd.Start(); err != nil {
return err
}
if m.eng != nil {
m.eng.Quit()
}
return nil
}