# console

`import "github.com/matjam/bunyip/console"`

Package console is the engine's in-game debug console: a drop-down command line over the top of the view, and a window of panels that show what every part of the engine is doing.

To turn it on, set Config.Console. The engine draws it after the game and the debug overlay.

The engine makes the console, attaches what it owns (the graphics context, the mixer, the input state and the frame timings) and hands it to the game on Context.Console. A game that wants a console of its own shape calls New instead and calls its Draw method explicitly.

### The console {#hdr-The_console}

The backquote key opens and closes the drop-down (Options.Key chooses another). While it is open it takes the keyboard: the game asks Console.Open and skips its own key handling for that update. The panel holds the log and a command line with history on the up and down arrows, completion on Tab, and paging with PageUp and PageDown.

	func (g *game) Update(ctx *engine.Context) error {
		if ctx.Console.Open() {
			return nil // the console has the keyboard
		}
		...
	}

Commands are registered with Register and read their arguments as strings. The built-in commands are help, echo, clear, quit, screenshot, exec, bind, unbind, binds, fps, stats, log, timescale, panels, set, get and vars.

Variables are registered with Float, Int, Bool, String or Var and edited with set and get, so a game exposes its tunables without writing commands for them:

	ctx.Console.Float("player.speed", &g.speed, "how fast the player runs")

### The panels {#hdr-The_panels}

F4, or the panels command, opens a resizable window of tabs: Engine (frame timings, profile scopes and draw counts), Graphics (the live post-processing settings and the GPU resources), Entities (the attached worlds, their entities, components, resources and systems), Physics (bodies, contacts, joints, solver settings and collider drawing), Audio (voices, buses and the listener), Input (keys, pointer, gamepads and action maps) and Services (whatever the game attached).

The engine attaches what it owns. A game attaches its own with one call each: Attach for an entity world, AttachActions for an action map, AttachInfo for a line of text in the Services tab and AttachLinks for a network connection's link statistics.

### Logging {#hdr-Logging}

With Config.Console the engine tees the log through the console, so every slog record shows in the panel as well as wherever it was going. The log command sets the lowest level captured from then on. A console a game builds itself installs the tee with Handler.

Every method works on a nil Console and does nothing, so the calls a game makes stay put when Config.Console is off and the field is nil.

## Types

<a id="Command"></a>

<a id="Command.Name"></a>

<a id="Command.Help"></a>

<a id="Command.Fn"></a>

### Command

```go
type Command struct {
	Name string                              // command token entered at the prompt
	Help string                              // one-line usage and description
	Fn   func(args []string) (string, error) // synchronous callback on the Run caller
}
```

Command is one console command. Fn reads the arguments after the command's name and returns the text to print, which may be empty, or an error, which prints in red.

<a id="Console"></a>

### Console

```go
type Console struct {
	// contains filtered or unexported fields
}
```

Console is one debug console: its output, its commands and variables, and the panels. Register commands and variables from any goroutine; draw it from the goroutine that runs the loop. Command callbacks and registered variables are accessed on that loop goroutine. Registration protects the registry, not the pointed-to game values; synchronize any access from other goroutines. Attachments, open state, Run and Draw also belong to the loop goroutine.

<a id="New"></a>

#### New

```go
func New(opts Options) *Console
```

New makes a console. Nothing is drawn until Draw is called, and the font and interface context are made the first time it is.

Example:

A console outside the engine's Config.Console: build it, tee the log through it, and hand it the frame every Draw.

```go
package main

import (
	"log/slog"

	"github.com/matjam/bunyip/console"
	"github.com/matjam/bunyip/input"
)

func main() {
	con := console.New(console.Options{Key: input.KeyF1, Height: 0.5})
	log := slog.New(con.Handler(slog.Default().Handler()))
	log.Info("the console is up")
	// Then, last of all in Draw:
	//	con.Draw(ctx)
}
```

<a id="Console.Attach"></a>

#### Console.Attach

```go
func (c *Console) Attach(name string, w *ecs.World)
```

Attach adds an entity world to the Entities and Physics panels under a name. Attach several worlds and the panels show a row of them to pick from. Attaching the same name again replaces that world.

<a id="Console.AttachActions"></a>

#### Console.AttachActions

```go
func (c *Console) AttachActions(name string, a *input.Actions)
```

AttachActions adds an action map to the Input panel, which lists every bound action with its sources and its value this frame.

<a id="Console.AttachInfo"></a>

#### Console.AttachInfo

```go
func (c *Console) AttachInfo(name string, text func() string)
```

AttachInfo adds a line to the Services panel, redrawn from text every frame. It is how a game shows a service the console knows nothing about: the locale, a save slot, a scheduler's timer count.

	con.AttachInfo("locale", func() string { return tr.Lang() })
	con.AttachInfo("timers", func() string { return strconv.Itoa(sched.Pending()) })

<a id="Console.AttachLinks"></a>

#### Console.AttachLinks

```go
func (c *Console) AttachLinks(name string, links func() []Link)
```

AttachLinks adds a connection's links to the Services panel. The console does not depend on the network package, so the game reads the statistics it wants shown:

	con.AttachLinks("server", func() []console.Link {
		var out []console.Link
		for _, a := range peer.Peers() {
			s, _ := peer.Stats(a)
			out = append(out, console.Link{Peer: a.String(), RTT: s.RTT,
				Loss: s.Loss, Pending: s.Pending, Connected: s.Connected})
		}
		return out
	})

<a id="Console.Bool"></a>

#### Console.Bool

```go
func (c *Console) Bool(name string, p *bool, help string)
```

Bool registers a bool the set command edits; set takes true, false, 1, 0, on or off.

<a id="Console.Clear"></a>

#### Console.Clear

```go
func (c *Console) Clear()
```

Clear empties the output.

<a id="Console.Commands"></a>

#### Console.Commands

```go
func (c *Console) Commands() []Command
```

Commands returns every registered command, sorted by name.

<a id="Console.Destroy"></a>

#### Console.Destroy

```go
func (c *Console) Destroy()
```

Destroy frees the font the console made for itself. The engine calls it before the graphics context goes; a game that passed its own font in Options keeps that font.

<a id="Console.Draw"></a>

#### Console.Draw

```go
func (c *Console) Draw(h Host) error
```

Draw runs one frame of console: the toggle keys, the key bindings, the command line while it is open, and the panels. Config.Console calls this automatically. For a console created with New, call it last in the game's Draw so it sits above everything else. It draws nothing while the console is closed and no panel window is open.

<a id="Console.Float"></a>

#### Console.Float

```go
func (c *Console) Float(name string, p *float32, help string)
```

Float registers a float32 the set command edits, for a tunable a game wants to try values for while it runs:

	con.Float("player.speed", &g.speed, "how fast the player runs")

<a id="Console.GetVar"></a>

#### Console.GetVar

```go
func (c *Console) GetVar(name string) (string, bool)
```

GetVar returns a variable's value as text, and false when no variable has that name.

<a id="Console.Handler"></a>

#### Console.Handler

```go
func (c *Console) Handler(next slog.Handler) slog.Handler
```

Handler returns a slog.Handler that passes records on to next and copies those at the console's level and above into its output, so the game's log shows in the console as well as wherever it was going. The engine installs it when Config.Console is set; a game building its own console installs it itself:

	con := console.New(console.Options{})
	cfg.Log = slog.New(con.Handler(slog.Default().Handler()))

Records are captured as they arrive, so lowering the threshold with the log command shows more from then on and never rewrites what is already there. Handle is safe to call from any goroutine.

<a id="Console.Int"></a>

#### Console.Int

```go
func (c *Console) Int(name string, p *int, help string)
```

Int registers an int the set command edits.

<a id="Console.Level"></a>

#### Console.Level

```go
func (c *Console) Level() slog.Level
```

Level is the lowest log level the console captures.

<a id="Console.Lines"></a>

#### Console.Lines

```go
func (c *Console) Lines() []string
```

Lines returns the output kept, oldest first, for tests and for a game that shows the log somewhere of its own.

<a id="Console.Open"></a>

#### Console.Open

```go
func (c *Console) Open() bool
```

Open reports whether the drop-down is showing. A game checks it in Update and leaves the keyboard alone while it is true. Toggle keys are processed in Draw, so Update observes the state from the preceding Draw. The debug panels alone do not make Open true.

<a id="Console.Print"></a>

#### Console.Print

```go
func (c *Console) Print(text string)
```

Print adds a line of output. It is safe to call from any goroutine.

<a id="Console.Printf"></a>

#### Console.Printf

```go
func (c *Console) Printf(format string, args ...any)
```

Printf adds a formatted line of output, as fmt.Sprintf writes it.

<a id="Console.Register"></a>

#### Console.Register

```go
func (c *Console) Register(name, help string, fn func(args []string) (string, error))
```

Register adds a command, replacing any command of the same name. Help is the one-line description the help command prints:

	con.Register("give", "give <item> [count]: add an item", func(args []string) (string, error) {
		...
	})

Example:

A game registers its commands, variables and worlds once, from Init.

```go
package main

import (
	"fmt"

	"github.com/matjam/bunyip/console"
	"github.com/matjam/bunyip/ecs"
	"github.com/matjam/bunyip/input"
)

// In a game these come from the game's own state; the console itself
// comes from engine.Context.Console once Config.Console is set.
var (
	con     *console.Console
	world   *ecs.World
	actions *input.Actions
	speed   float32 = 6
	noclip  bool
)

func main() {
	con.Register("give", "give <item> [count]: add an item to the pack",
		func(args []string) (string, error) {
			if len(args) == 0 {
				return "", fmt.Errorf("give: needs an item")
			}
			return "gave " + args[0], nil
		})
	con.Float("player.speed", &speed, "how fast the player runs")
	con.Bool("player.noclip", &noclip, "walk through walls")
	con.Attach("world", world)
	con.AttachActions("player", actions)
	con.AttachInfo("save slot", func() string { return "slot 2" })
}
```

<a id="Console.Run"></a>

#### Console.Run

```go
func (c *Console) Run(line string)
```

Run parses and runs one command line, printing what it returns. An unknown command is an error. Call it from the goroutine that draws.

<a id="Console.SetLevel"></a>

#### Console.SetLevel

```go
func (c *Console) SetLevel(l slog.Level)
```

SetLevel changes the lowest level captured, as the log command does.

<a id="Console.SetOpen"></a>

#### Console.SetOpen

```go
func (c *Console) SetOpen(open bool)
```

SetOpen opens or closes the drop-down.

<a id="Console.SetVar"></a>

#### Console.SetVar

```go
func (c *Console) SetVar(name, text string) error
```

SetVar parses text into a registered variable.

<a id="Console.String"></a>

#### Console.String

```go
func (c *Console) String(name string, p *string, help string)
```

String registers a string the set command edits.

<a id="Console.Toggle"></a>

#### Console.Toggle

```go
func (c *Console) Toggle()
```

Toggle flips the drop-down open or shut, as the console's key does.

<a id="Console.Var"></a>

#### Console.Var

```go
func (c *Console) Var(name string, v Var, help string)
```

Var registers a variable of any type, replacing one of the same name. The console never copies the value: it reads and writes through v whenever the game asks, so a variable stays live.

<a id="Console.VarNames"></a>

#### Console.VarNames

```go
func (c *Console) VarNames() []string
```

VarNames returns every registered variable's name, sorted.

<a id="Console.Worlds"></a>

#### Console.Worlds

```go
func (c *Console) Worlds() []string
```

Worlds returns the attached worlds' names, in the order they were attached.

<a id="Frame"></a>

<a id="Frame.Gfx"></a>

<a id="Frame.Input"></a>

<a id="Frame.Audio"></a>

<a id="Frame.Log"></a>

<a id="Frame.Clipboard"></a>

<a id="Frame.Width"></a>

<a id="Frame.Height"></a>

<a id="Frame.Delta"></a>

<a id="Frame.Time"></a>

<a id="Frame.FrameCount"></a>

<a id="Frame.Stats"></a>

<a id="Frame.Screenshot"></a>

<a id="Frame.Quit"></a>

<a id="Frame.SetTimeScale"></a>

<a id="Frame.TimeScale"></a>

### Frame

```go
type Frame struct {
	Gfx   *gfx.Graphics // drawing context; required by Draw
	Input *input.State  // frame input; required by Draw
	Audio *audio.Mixer  // optional mixer for the Audio panel
	Log   *slog.Logger  // host logger, available to console integrations

	// Clipboard is what the panels' text fields cut, copy and paste
	// through; the engine's Context satisfies it.
	Clipboard ui.Clipboard

	// Width and Height are the view's size in view units.
	Width, Height float32
	// Delta is the seconds the last update covered, Time the seconds the
	// game has run, and FrameCount the frames drawn.
	Delta, Time float64
	FrameCount  uint64 // number of frames drawn

	// Stats are the previous frame's timings and counts.
	Stats Stats

	// Screenshot writes the next drawn frame to a PNG at the path.
	Screenshot func(path string)
	// Quit ends the loop.
	Quit func()
	// SetTimeScale and TimeScale drive the timescale command.
	SetTimeScale func(scale float64)
	TimeScale    func() float64 // read the current game-time multiplier
}
```

Frame is the engine state one console frame draws from. The engine fills it in from its own context, so a game never builds one; a game driving a console outside the engine's loop fills in what it has. Every field may be zero, and the console leaves out what it is not given: without Screenshot the screenshot command reports an error, and without Audio the audio panel says so. Draw does nothing unless both Gfx and Input are non-nil.

<a id="Host"></a>

<a id="Host.ConsoleFrame"></a>

### Host

```go
type Host interface {
	ConsoleFrame() Frame
}
```

Host is anything that can describe the frame the console draws in. The engine's \*engine.Context implements it, so a game passes its context straight to Draw.

<a id="Link"></a>

<a id="Link.Peer"></a>

<a id="Link.RTT"></a>

<a id="Link.Loss"></a>

<a id="Link.Pending"></a>

<a id="Link.Connected"></a>

### Link

```go
type Link struct {
	Peer      string        // who the link is to
	RTT       time.Duration // round trip time
	Loss      float32       // fraction of packets lost, 0 to 1
	Pending   int           // messages sent and not yet acknowledged
	Connected bool          // false marks the peer as down in the panel
}
```

Link is one network link as the Services panel shows it.

<a id="Options"></a>

<a id="Options.Key"></a>

<a id="Options.PanelKey"></a>

<a id="Options.Height"></a>

<a id="Options.Lines"></a>

<a id="Options.Level"></a>

<a id="Options.Font"></a>

<a id="Options.Theme"></a>

<a id="Options.Read"></a>

### Options

```go
type Options struct {
	// Key opens and closes the drop-down; zero means the backquote key.
	Key input.Key
	// PanelKey opens and closes the window of debug panels; zero means F4.
	PanelKey input.Key
	// Height is the fraction of the view the drop-down covers, 0 to 1;
	// zero means 0.4.
	Height float32
	// Lines is how many lines of output are kept; zero means 1000.
	Lines int
	// Level is the lowest log level captured by Handler; zero means Info.
	// The log command changes it while the game runs.
	Level slog.Level
	// Font draws the console; nil makes one from the built-in font at 13
	// view units the first time the console draws.
	Font *gfx.Font
	// Theme styles the panels; nil uses the dark theme over Font.
	Theme *ui.Theme
	// Read is where exec reads scripts from: pass an asset.FS's Read
	// method to run scripts out of a pack file. Nil reads the file system.
	Read func(name string) ([]byte, error)
}
```

Options configure a console. Every zero value means the default noted.

<a id="Prefabs"></a>

### Prefabs

```go
type Prefabs map[string]*ecs.Prefab
```

Prefabs is a named set of prefabs the Entities panel can spawn. Set it as a resource on a world and the panel lists the names with a button each:

	w.SetResource(console.Prefabs{"goblin": goblinPrefab})

<a id="Scope"></a>

<a id="Scope.Name"></a>

<a id="Scope.MS"></a>

### Scope

```go
type Scope struct {
	Name string  // profile section or GPU pass name
	MS   float64 // duration in milliseconds
}
```

Scope is one timed section of game code, as Context.Profile records it.

<a id="Stats"></a>

<a id="Stats.FPS"></a>

<a id="Stats.FrameMS"></a>

<a id="Stats.UpdateMS"></a>

<a id="Stats.DrawMS"></a>

<a id="Stats.PresentMS"></a>

<a id="Stats.Updates"></a>

<a id="Stats.Scopes"></a>

<a id="Stats.GPUFrameMS"></a>

<a id="Stats.GPU"></a>

<a id="Stats.DrawBudget"></a>

### Stats

```go
type Stats struct {
	FPS       float64 // frames per second over the last second
	FrameMS   float64 // wall time from one frame's start to the next
	UpdateMS  float64 // time spent in Update calls this frame
	DrawMS    float64 // time spent in Draw
	PresentMS float64 // time submitting and waiting on the GPU
	Updates   int     // Update calls this frame
	Scopes    []Scope // profile scopes recorded this frame

	// GPUFrameMS is how long the GPU spent on a recent frame and GPU is
	// that time by pass. Both come from timestamp queries read back a
	// frame or two later, so they lag the frame on screen, and both are
	// zero and empty on a device without timestamp queries.
	GPUFrameMS float64
	GPU        []Scope // GPU duration by pass in milliseconds

	// DrawBudget is the draw call count a frame should stay under, from
	// Config.DrawBudget; zero means no budget was set.
	DrawBudget int
}
```

Stats are the frame timings and counts the engine panel shows. They mirror engine.Stats, which the engine copies in, so the console does not depend on the root package.

<a id="Var"></a>

<a id="Var.String"></a>

<a id="Var.Set"></a>

### Var

```go
type Var interface {
	String() string
	Set(text string) error
}
```

Var is a console variable of a type the console has no helper for: a colour, an enumeration, a value behind a lock. String is what get prints and Set parses what set is given.
