Expand description
Shuyi Engine’s macroquad backend (ADR-0008 §2, game ADR-0004): the
window, blitting the frame (render), the keyboard (keys) and
controllers (pads), the audio player and storage (storage),
behind shuyi_platform’s Platform, and the loop (run) that calls
shuyi_platform::App::frame once per frame of macroquad’s, on desktop,
the web and the Steam Deck.
On desktop, read_folder reads a folder on disk (a player’s mod) into
a bundle layer the game lays over its own files. The web has no
folders, so it isn’t there: there page::page_layer reads the files
the page holds into one instead. In a build with debug tools the folders it
reads are watched, and watched_layer gives the one cargo shuyi run --watch names (ADR-0071).
A game on the short path (ADR-0036) runs from a plain main, with
run_game and its BasicGame:
fn main() {
shuyi_app::run_game(game::game());
}A game of its own shape keeps macroquad’s entry point, which has to be
in the binary, and hands over its AppConfig and how it starts
(Start):
fn window_conf() -> macroquad::window::Conf {
shuyi_app::window_conf(&WINDOW)
}
fn look(game: &MyGame) -> Look<'_> {
Look::new(&game.font, &game.images)
}
#[macroquad::main(window_conf)]
async fn main() {
let start = Start::new(Card::text("My Game"), look, |setup| {
let ctx = setup.ctx(my_ctx()?);
Ok(Game::new(ctx, Box::new(Title), GameHooks::default()))
});
shuyi_app::run(config(), start).await;
}With the link feature, a debug build listens for the live link
(ADR-0047), so shuyi attach reaches the game running in its window:
run it with SHUYI_LINK=0 (a free port; or a port of your choosing),
and its log says shuyi link: listening on 127.0.0.1:<port>, token <token>. SHUYI_LINK_TOKEN sets the token instead of a random one. A
release build, or one without the feature, never listens; on the web
the feature does nothing yet.
With the capture feature, a desktop build runs a plan instead of the
player’s input when SHUYI_CAPTURE names one, compares the window with
the raster on the frames it names, and exits (the capture module, ADR-0052):
SHUYI_CAPTURE=examples/hello-grid/capture/frames.ron cargo run -p hello-grid --features captureIt writes each capture’s pictures and capture.txt under
SHUYI_CAPTURE_OUT (target/capture unless set), and exits 0 when
every capture is within the plan’s tolerance, 1 when one isn’t, and 2
when the plan can’t run. Without the feature the variable is never
read. On the web a build with the feature always runs the plan
capture.ron beside the page, writes no files and logs each capture’s
line to the browser’s console instead (ticket 2019).
With the sound-probe feature, SoundProbeBackend names the sound
backend, for the example sound_probe that the parity gates record. No
game turns it on.
With the in-house-sound feature, sound goes through shuyi-sound’s mixer and
shuyi-sound-out instead of quad-snd.
A desktop debug build writes its frames’ times as it runs when
SHUYI_PERF names a file (SHUYI_PERF=perf.jsonl cargo run): its log
names the file, and after each frame it writes that frame’s record as a
JSON line (shuyi_ui::perf::PerfLine) and flushes, so tail -f, or
reading the file again from the last line read, follows it (ADR-0046).
A build without debug tools logs why and writes nothing; a file that
can’t be written is one warning, and the game goes on.
A panic writes a crash report through the game’s storage, beside its
saves (the crash module, ADR-0079): crash-<when>.ron in its data
folder on native, and in localStorage on the web. At its next start
the game hears of it once (shuyi_platform::Setup::last_crash).
AppConfig::crash names the game’s version in it, or turns it off.
A desktop debug build keeps its last sessions in sessions/ under the
game’s data folder (the sessions module, ADR-0074): the record of a run,
written when the game quits and when the debug menu’s “Save session” asks,
the newest ten kept. On the web that entry offers a download. shuyi run --session <file> plays one.
Every debug build counts the heap: this crate turns on
shuyi_alloc_count’s global feature, whose #[global_allocator]
counts the bytes the whole program asks for, every thread’s, and each
frame’s record gets the live bytes, their peak and that frame’s
allocations (shuyi_ui::perf::FrameRecord::heap). Its counts are
also kept by memory tag: the frame’s phases (load, update, draw,
paint, perf and app), the game’s spans, and each asset kind’s
decode (shuyi_alloc_count::tags). A release build has no counter. A game with a #[global_allocator] of its own (a binary
has one) opts out in its .cargo/config.toml:
[build]
rustflags = ["--cfg", "shuyi_no_heap_count"]On the web the game’s binary must link with --allow-undefined: the
JS plugins’ functions (crates/cli/web/gamepad.js’s, download.js’s,
page-files.js’s, quad-storage’s) are
imports the page provides, not link errors. A link argument only
reaches the binary from its own build script
(crates/cli/web/README.md shows how).
Re-exports§
pub use crate::crash::CrashReports;
Modules§
- crash
- Crash reports (ADR-0079): a panic hook that writes a
shuyi_platform::crash::CrashReportthrough the game’s storage, whichruninstalls before its first frame (AppConfig::crash). - keys
- Translation from macroquad key codes to
shuyi-ui’s hardware-agnosticKey, collecting a frame’s keyboard events asRawInputEvents, and hearing what the window’s focus is made from (WindowHeard). - pads
- Controllers (ticket 0219, game ADR-0034): reads every connected pad’s raw
state each frame, with gilrs on native and the browser’s Gamepad API
(through
crates/cli/web/gamepad.js) on web, and hands it toshuyi-ui’sPads, which holds all the rules (sticks as directions, the Switch-style swap, several pads as one). Likekeys.rs, this only translates, and ignores the pads while the window has no focus (pad_events). - page
- Files from the page, on the web: the text files the page a web build runs
in holds, read into a bundle layer (ADR-0004 §1) the game lays over its own
files, and the game’s problems with them sent back (
report). It is how a page that edits a game’s data beside it (the site’s playground) runs the game on its edits with no install. On every other target the page holds none. - render
- Blits a
GlyphBufferto the window with the font atlas (game ADR-0003): integer-scaled, centred, black letterbox. The buffer’s items are drawn scaled, in the order they were added:Underones after the cell backgrounds,Overones after the glyphs. A rectangle (game ADR-0018) is a solid fill; a sprite (game ADR-0038) is part of an image’s texture, or of a recoloured copy of it when the sprite isn’t painted in the image’s own colours (game ADR-0049). A buffer’s backdrop (game ADR-0048), a second buffer panned and zoomed, is drawn the same way inside its window, behind the console. A wide glyph (its continuation cell after it) is one quad over both its cells, as the raster paints it. A painted picture (ADR-0078) is a texture of its own, uploaded only in a frame whose revision moved (PaintedTextures, inpainted.rs), drawn with nearest-pixel sampling. - storage
- Platform
Storagebackends (ticket 0207, game ADR-0009): files in the OS data directory on native,localStorageon web.
Structs§
- AppConfig
- What a game tells the macroquad backend. How it starts, loads and is
painted is its
Start. Build it withAppConfig::new. - Icon
- A window icon: PNG files of 16, 32 and 64 pixels square. It is
shuyi_platform’s, which a game’sAppInfocarries (ADR-0061). A window’s icon: PNG files of 16, 32 and 64 pixels square, whichshuyi app-infodownscales from the game’s one icon (ADR-0061). Build it withWindowIcon::new. - Voice
Files - Where a game’s voice clips are (game ADR-0046): a folder shipped beside the game (not embedded), with a folder per language holding the manifest and the clips.
- Window
- The window a game opens. Build it with
Window::new: a struct literal doesn’t compile outside this crate, so a new field never breaks a game (ADR-0039).
Enums§
- Folder
Error - Why a folder could not be read into a layer (
read_folder).
Functions§
- read_
folder - Every file in the folder at
pathand the folders below it, as one layer namedname, each at its path relative to the folder,/-separated (data/crops.ron). A link to a file is read; a link to a folder is not followed. Read it once: the layer’s files are kept for the life of the program. - read_
voice_ manifest - The text of the voice manifest
fileindir, orNoneif there is none (no voices: not a problem).Errif the file is there and can’t be read. - run
- Runs a game until it quits, as
startsays: the first frame shows its loading card, the next loads it (or shows why it can’t: a wrong command line, a broken asset), and each frame after,Appreads the keyboard and controllers, steps the game, plays its sounds, music and voices and draws its frame. Closing the window lets the game save first. - run_
game - Runs a game on the short path until it quits, in a window of its
console at twice its size: its files become the process’s bundle, and
runstarts it asBasicGame::startsays, with no sounds, no voices and no system keyboard. Called from a plainfn main. Undercargo shuyi run --watchthe game’s files on disk are laid over the embedded ones (watched_layer). - watched_
layer - The folder
cargo shuyi run --watchnamed (WATCH_VAR) as a layer to lay over the game’s files, watched like any folderread_folderreads and shown under the folder’s name: in a build with debug tools, when it is set. A folder that can’t be read logs why, and the game runs on its embedded files. - window_
conf - The window settings for macroquad’s entry point
(
#[macroquad::main(window_conf)]).