Skip to main content

gasm_host/
session.rs

1//! A game ready to start: the module, its assets, parameters, storage and load
2//! options. [`crate::headless::run`] and `window::run` (feature `window`) drive it.
3
4use std::collections::HashMap;
5use std::time::Instant;
6
7use crate::assets::Assets;
8use crate::audio::AudioOut;
9use crate::angle::Angle;
10use crate::gfx::Gfx;
11use crate::host::{Game, Host, LoadOptions, Stop};
12use crate::net::Net;
13use crate::storage::Storage;
14
15pub struct Session {
16    /// shown in logs and the window title
17    pub name: String,
18    /// a .wasm module, or a .cwasm if `load.allow_precompiled`
19    pub wasm: Vec<u8>,
20    pub assets: Assets,
21    pub params: HashMap<String, String>,
22    pub allow_net: bool,
23    /// with `allow_net`: only these hosts (`--allow-net=a.org,*.b.org`; empty: all)
24    pub allow_hosts: Vec<String>,
25    /// gasm:fetch: live, recording or replaying responses (headless)
26    pub fetch: crate::fetch::FetchMode,
27    pub storage: Storage,
28    pub load: LoadOptions,
29    /// where ANGLE is (gasm:gl games): None searches the usual places (`angle::find`)
30    pub gl_lib: Option<std::path::PathBuf>,
31    /// gasm:gl on SwiftShader (software Vulkan) instead of the GPU
32    pub gl_software: bool,
33    /// `--watch-asset name=path`: files re-read as assets whenever they change
34    /// (create each watch before opening its file, so no change is missed)
35    pub watch_assets: Vec<crate::assets::AssetWatch>,
36}
37
38impl Session {
39    /// Whether the game imports gasm:gl (it then needs ANGLE to draw).
40    pub fn uses_gl(&self) -> bool {
41        crate::host::imports_module(&self.wasm, "gasm:gl", self.load.allow_precompiled)
42    }
43
44    /// ANGLE for this session's gasm:gl game: offscreen at `w`×`h`, or in a window.
45    pub fn open_gl(&self, window: Option<crate::angle::NativeWindow>, size: (u32, u32)) -> Result<Angle, String> {
46        let dir = crate::angle::find(self.gl_lib.as_deref())?;
47        let open = |software| match window {
48            Some(w) => Angle::window(&dir, w, software),
49            None => Angle::offscreen(&dir, size.0, size.1, software),
50        };
51        match open(self.gl_software) {
52            // no usable GPU (or driver): SwiftShader
53            Err(e) if !self.gl_software => {
54                eprintln!("[gasm] gl: {e}; trying SwiftShader (software)");
55                open(true)
56            }
57            r => r,
58        }
59    }
60
61    /// Instantiate the guest and run its init. `reproducible`: headless rules
62    /// (virtual time, fixed random sequence, hashing). `gl`: the GL that executes a
63    /// gasm:gl guest's calls (None: the null GL).
64    pub fn start(self, audio: Option<Box<dyn AudioOut>>, gfx: Gfx, gl: Option<Angle>, reproducible: bool, hashing: bool) -> Result<Game, Stop> {
65        let policy = crate::net::NetPolicy::new(self.allow_net, self.allow_hosts);
66        let mut host = Host::new(self.assets, self.params, audio, gfx, Net::with_policy(policy.clone()), self.storage);
67        host.fetch = crate::fetch::Fetch::new(policy, self.fetch);
68        if let Some(a) = gl {
69            eprintln!("[gasm] gl: {}", a.renderer);
70            host.gl.attach(a);
71        }
72        if reproducible {
73            host.set_reproducible();
74        }
75        host.hashing = hashing;
76        let t0 = Instant::now();
77        let mut game = Game::load(&self.wasm, host, self.load)?;
78        for w in self.watch_assets {
79            game.add_watch(w);
80        }
81        eprintln!("[gasm] loaded {} in {:.0} ms", self.name, t0.elapsed().as_secs_f64() * 1000.0);
82        Ok(game)
83    }
84}