diff --git a/cmd/asobi/main.go b/cmd/asobi/main.go index e4a4453..4e268f5 100644 --- a/cmd/asobi/main.go +++ b/cmd/asobi/main.go @@ -89,8 +89,9 @@ Usage: asobi logout Clear stored credentials asobi whoami Show current credential info asobi init [dir] Scaffold a starter Lua game - asobi init [dir] --template - Scaffold a runnable demo (defold|godot|unity) + asobi init [dir] --template + Scaffold a runnable project: + defold|godot|unity (client demo) or backend (server only) asobi dev [--port N] [--dir ] Run a local backend for your Lua game (Docker) asobi games List your tenant's games @@ -688,7 +689,7 @@ func cmdInit() { if engine != "" { t, ok := template.Get(engine) if !ok { - fatal("unknown template %q; available: %s", engine, strings.Join(template.Engines(), ", ")) + fatal("unknown template %q; available: %s", engine, strings.Join(template.Names(), ", ")) } fmt.Printf("Fetching the %s template into %s ...\n", t.Name, dir) created, err := template.Fetch(engine, dir) @@ -705,7 +706,12 @@ func cmdInit() { if dir != "." { step("cd " + dir) } - step("Open README.md - it walks through starting the backend and opening the project in " + t.Name + ".") + if engine == "backend" { + step("docker compose up -d (asobi_lua image + Postgres on :8084)") + step("Open README.md - it covers the managed (asobi deploy) vs local (compose) paths and adding modes in lua/config.lua.") + } else { + step("Open README.md - it walks through starting the backend and opening the project in " + t.Name + ".") + } return } diff --git a/internal/scaffold/scaffold.go b/internal/scaffold/scaffold.go index 1baef02..f60f516 100644 --- a/internal/scaffold/scaffold.go +++ b/internal/scaffold/scaffold.go @@ -63,9 +63,19 @@ The game logic lives in ` + "`lua/match.lua`" + `. The engine loads it as the ` + "`handle_input`, `tick` and `get_state`. `match_size`" + ` sets how many players are needed to start a match. -## Deploy +## Run it locally -Run these from this directory. +No account, no keys - runs the asobi_lua image + Postgres in Docker: + +` + "```bash" + ` +asobi dev +` + "```" + ` + +Edit ` + "`lua/`" + ` and it hot-reloads. API + WebSocket on http://localhost:8084. + +## Deploy to Asobi Cloud + +Managed hosting - this needs a login: ` + "```bash" + ` asobi login @@ -74,6 +84,29 @@ asobi create # e.g. asobi create prod asobi deploy lua ` + "```" + ` +## Multiple game modes + +To run more than the ` + "`default`" + ` mode, add a ` + "`lua/config.lua`" + ` manifest +mapping mode names to files: + +` + "```lua" + ` +return { + default = "match.lua", + ranked = "ranked.lua" +} +` + "```" + ` + +Clients pick a mode with ` + "`matchmaker.add {mode = \"ranked\"}`" + `. + +## A full backend example + +Want the whole picture - Docker Compose, the mode manifest, all the +` + "`ASOBI_*`" + ` env vars in one runnable repo? + +` + "```bash" + ` +asobi init mybackend --template backend +` + "```" + ` + ## Connect a client Defold quickstart: https://github.com/widgrensit/asobi-defold diff --git a/internal/scaffold/scaffold_test.go b/internal/scaffold/scaffold_test.go index 6097ef5..7ccf935 100644 --- a/internal/scaffold/scaffold_test.go +++ b/internal/scaffold/scaffold_test.go @@ -50,6 +50,11 @@ func TestInitCreatesWorkingGame(t *testing.T) { t.Fatalf("README.md missing deploy step %q", step) } } + for _, hint := range []string{"asobi dev", "config.lua", "--template backend"} { + if !strings.Contains(string(readme), hint) { + t.Fatalf("README.md missing %q", hint) + } + } } func TestInitRefusesToOverwrite(t *testing.T) { diff --git a/internal/template/template.go b/internal/template/template.go index e8570fe..93aeb17 100644 --- a/internal/template/template.go +++ b/internal/template/template.go @@ -1,7 +1,8 @@ -// Package template scaffolds a runnable client+backend game project by fetching -// one of the asobi demo repositories at a pinned commit. The demos are the single -// source of truth (no parallel templates to drift); each carries its own README -// documenting how to run the backend and open the project in its engine. +// Package template scaffolds a runnable game project by fetching one of the asobi +// demo repositories at a pinned commit: an engine template (client + backend) or +// the backend template (server-Lua backend only). The repos are the single source +// of truth (no parallel templates to drift); each carries its own README +// documenting how to run the backend and, for engine templates, open the project. package template import ( @@ -27,9 +28,10 @@ type Template struct { } var registry = map[string]Template{ - "defold": {Repo: "asobi-defold-demo", Ref: "v0.2.0", Name: "Defold"}, - "godot": {Repo: "asobi-godot-demo", Ref: "v0.2.0", Name: "Godot"}, - "unity": {Repo: "asobi-unity-demo", Ref: "v0.2.0", Name: "Unity"}, + "defold": {Repo: "asobi-defold-demo", Ref: "v0.2.0", Name: "Defold"}, + "godot": {Repo: "asobi-godot-demo", Ref: "v0.2.0", Name: "Godot"}, + "unity": {Repo: "asobi-unity-demo", Ref: "v0.2.0", Name: "Unity"}, + "backend": {Repo: "sdk_demo_backend", Ref: "v0.1.0", Name: "Backend"}, } const ( @@ -37,8 +39,9 @@ const ( maxBytes = 500 << 20 // 500 MiB, generous for a Unity demo ) -// Engines returns the known template engine keys, sorted. -func Engines() []string { +// Names returns the known template names, sorted (e.g. defold, godot, unity, +// backend). +func Names() []string { keys := make([]string, 0, len(registry)) for k := range registry { keys = append(keys, k) @@ -58,7 +61,7 @@ func Get(engine string) (Template, bool) { func Fetch(engine, dir string) ([]string, error) { t, ok := registry[engine] if !ok { - return nil, fmt.Errorf("unknown template %q; available: %s", engine, strings.Join(Engines(), ", ")) + return nil, fmt.Errorf("unknown template %q; available: %s", engine, strings.Join(Names(), ", ")) } if err := requireEmptyDir(dir); err != nil { return nil, err diff --git a/internal/template/template_test.go b/internal/template/template_test.go index d4b4409..5b32a37 100644 --- a/internal/template/template_test.go +++ b/internal/template/template_test.go @@ -51,11 +51,11 @@ func makeTarGz(t *testing.T, entries []entry) []byte { return buf.Bytes() } -func TestEnginesKnown(t *testing.T) { - got := Engines() - want := []string{"defold", "godot", "unity"} +func TestNamesKnown(t *testing.T) { + got := Names() + want := []string{"backend", "defold", "godot", "unity"} if !reflect.DeepEqual(got, want) { - t.Fatalf("Engines() = %v, want %v", got, want) + t.Fatalf("Names() = %v, want %v", got, want) } for _, e := range want { if _, ok := Get(e); !ok {